Swift 正在后台队列上读取更新的域ThreadSafeReference

Swift 正在后台队列上读取更新的域ThreadSafeReference,swift,realm,Swift,Realm,根据: 当您最初在线程上打开领域时,其状态将基于最近成功的写入提交 : 未能解析ThreadSafeReference将导致域的源版本被固定,直到解除分配该引用为止 这在一起使用时会产生一些混乱的结果。如果ThreadSafeReference在后台队列上异步解析,它将显示旧的(固定的)值。但如果ThreadSafeReference被同步解析,它将显示更新的值 假设我有一个简单的领域对象:MyObject,它有一个名称属性: //安装程序,创建名为“a”的对象 let queue=Dispat

根据:

当您最初在线程上打开领域时,其状态将基于最近成功的写入提交

:

未能解析ThreadSafeReference将导致域的源版本被固定,直到解除分配该引用为止

这在一起使用时会产生一些混乱的结果。如果
ThreadSafeReference
在后台队列上异步解析,它将显示旧的(固定的)值。但如果
ThreadSafeReference
被同步解析,它将显示更新的值

假设我有一个简单的领域对象:
MyObject
,它有一个
名称
属性:

//安装程序,创建名为“a”的对象
let queue=DispatchQueue(标签:“com.app.my”,qos:。默认值,属性:[])
设obj=MyObject(名称:“a”)
尝试realm.write{realm.add(obj)}
//获取引用(在更新之前故意创建)
let ref=螺纹安全参考(至:obj)
//将名称更新为“b”(主队列或其他队列上的.sync)
试试realm.write{
obj.name=“b”
}
//后台线程上的异步解析
queue.async{
让r=try!Realm()
让o=r.resolve(ref)!
xctasertequal(“b”,o.name)//失败,o.name==“a”
r、 刷新()
xctasertequal(“b”,o.name)//成功
}
与之相比:

// setup, create object with name: "a"
let queue = DispatchQueue(label: "com.app.my", qos: .default, attributes: [])
let obj = MyObject(name: "a")
try realm.write { realm.add(obj) }

// get reference (deliberately created before update)
let ref = ThreadSafeReference(to: obj)

// update name to "b" (main queue, or .sync on other queue)
try realm.write {
    obj.name = "b"
}

// sync resolve on background thread
queue.sync {
    let r = try! Realm()
    let o = r.resolve(ref)!

    XCTAssertEqual("b", o.name) // succeeds
}
如果使用
queue.sync
而不是
queue.async
域,则不需要手动刷新,解析的引用立即显示更新的
“b”
名称。当查询领域以查找
MyObject
而不是解析引用时,也会出现这种情况


那么
ThreadSafeReferences
的行为到底是什么呢?看起来它有时会保持固定值,但有时不会。在这种情况下,当域应基于“最近成功写入提交”时,为什么有必要刷新域?

不久前看到了相同的问题,最近找到了发生这种情况的原因:

[引用:

。。。我们在主线程领域中添加了新对象,同时 正在后台线程上轮询以获取更改,但后台 未通知线程领域,即使其
autorefresh
属性为
true
答案是“后台线程没有 激活运行循环,您需要按顺序手动调用
refresh()
更新至最新版本,即使
autorefresh
设置为
true

[报价结束]

你可以在这里找到原来的帖子


因此,答案是,为了查看更改,如果我们在没有运行循环的线程中操作,我们必须手动调用Realm.refresh()函数

我不确定我是否遵循了这个问题;如果您想在后台线程上运行一个耗时的函数(比如写入2Gb的数据),您可以将其封装在后台调度队列中,如下所示
DispatchQueue(标签:“background”).async{
这是您所拥有的。但是,在该队列中,所有数据都应该封装在自动释放池中,以便在不使用时可以释放对象。此外,默认值比后台任务具有更高的优先级,所以这就是您要使用的任务?同样,当XCTASERTEQUAL失败时,失败的性质是什么?还有…placeme
let ref=ThreadSafeReference(to:obj)
的nt应该在第二次写入之后-然后,XCTAssertEqual调用将显示它们相等。@Jay这只是展示我的问题的一个示例(我想,
autorelease
.default
等都是实现细节,与这个特定问题并不相关,但您当然是正确的)。断言失败,因为
o.name
仍然是
“a”
因此ref指向旧数据。问题是:这是预期的行为吗?因为在使用
.sync
时,它被更新为
“b”
。有什么区别吗?为什么域/ref在一种情况下是最新的,而在另一种情况下不是最新的?创建ref的位置是在更新为
“b”之前故意设置的
。措辞不同:“状态将基于最近成功的写提交”并非在所有情况下都是如此?ref可能会在创建时解析固定数据,是否有任何规则指定此行为?我一直在思考这个问题,作为猜测,.sync确保工作现在完成,在运行循环中执行它(因此刷新领域)。而async稍后执行工作,这可能不在同一运行循环上。换句话说,同步(阻塞和等待)在任务完成时返回,而异步(分派和继续)函数立即返回,分派任务但不等待它完成(不在运行循环上)。但是,我可能完全错了。谢谢!我知道后台域不会自动刷新,但我不认为这里会发生这种情况,因为后台域是在写入/更新之后创建的。特别是“当您最初在线程上打开域时,其状态将基于最近成功的写入提交”似乎站不住脚。