线程之间的领域数据不一致
Inconsistent realm data between threads
我有一个案例,我需要根据用户请求用一系列对象填充领域。为了填充领域,我进行了网络调用,然后将对象写入领域。写入完成后,后台线程调用回调,切换到主线程处理结果。我更愿意使用领域通知,但这是一个非常具体的用例,通知不是一个选项。
我无法分享我的完整项目,但我能够通过示例项目重现此问题。这是我的数据模型。
class Owner: Object {
@objc dynamic var uuid: String = ""
@objc dynamic var name: String = ""
convenience init(uuid: String, name: String) {
self.init()
self.uuid = uuid
self.name = name
}
func fetchDogs() -> Results<Dog>? {
return realm?.objects(Dog.self).filter("ownerID == %@", uuid)
}
override class func primaryKey() -> String? {
return "uuid"
}
}
class Dog: Object {
@objc dynamic var uuid: String = ""
@objc dynamic var name: String = ""
@objc dynamic var ownerID: String = ""
func fetchOwner() -> Owner? {
return realm?.object(ofType: Owner.self, forPrimaryKey: ownerID)
}
convenience init(uuid: String, name: String, ownerID: String) {
self.init()
self.uuid = uuid
self.name = name
self.ownerID = ownerID
}
override class func primaryKey() -> String? {
return "uuid"
}
}
我意识到 List
适合 Owner
-> Dog
关系,但在这个项目中这不是一个选项。
示例项目中有两个我们关心的按钮。一种是将一些 Dog
对象添加到领域并将它们与 Owner
UUID 相关联。另一个按钮删除属于所有者的所有 Dog
对象。
基本流程是这样的:
- 删除主人的所有狗
- 在后台线程上创建一组新的狗并将它们添加到领域。
- 调用回调
- 切换回主线程
- 打印主人的所有狗
狗十有八九打印成功。但是,有时 realm 表示没有与当前所有者关联的狗。这可能是什么原因?我的视图控制器逻辑如下。
编辑:我最后要提的是这个问题只能在物理设备上重现。我无法在模拟器上重现它。这让我想知道这是否是一个领域错误。
// MARK: - Props and view controller life cycle
class ViewController: UIViewController {
var owner: Owner?
let ownerUUID = UUID().uuidString
override func viewDidLoad() {
super.viewDidLoad()
owner = makeOwner()
}
}
// MARK: - Action methods
extension ViewController {
@IBAction func onDeletePressed(_ sender: UIButton) {
print("Deleting...")
deleteDogs()
}
@IBAction func onRefreshPressed(_ sender: UIButton) {
print("Creating...")
createDogs {
DispatchQueue.main.async {
self.printDogs()
}
}
}
@IBAction func onPrintPressed(_ sender: UIButton) {
printDogs()
}
}
// MARK: - Helper methods
extension ViewController {
private func makeOwner() -> Owner? {
let realm = try! Realm()
let owner = Owner(uuid: ownerUUID, name: "Bob")
try! realm.write {
realm.add(owner)
}
return owner
}
private func deleteDogs() {
guard let dogs = owner?.fetchDogs() else { return }
let realm = try! Realm()
try! realm.write {
realm.delete(dogs)
}
}
private func createDogs(completion: @escaping () -> Void) {
DispatchQueue(label: "create.dogs.background").async {
autoreleasepool {
let realm = try! Realm()
let dogs = [
Dog(uuid: UUID().uuidString, name: "Fido", ownerID: self.ownerUUID),
Dog(uuid: UUID().uuidString, name: "Billy", ownerID: self.ownerUUID),
Dog(uuid: UUID().uuidString, name: "Sally", ownerID: self.ownerUUID),
]
try! realm.write {
realm.add(dogs)
}
}
completion()
}
}
private func printDogs() {
guard let dogs = owner?.fetchDogs() else { return }
print("Dogs: \(dogs)")
}
}
狗似乎是在单独的线程上创建的; 'background thread' 并且因此它不会有 运行 循环 - 所以两个线程正在查看数据的不同状态。
在没有 运行 循环的线程上创建对象时,必须手动调用 Realm.refresh() 才能将事务推进到最新状态。
上的文档
我有一个案例,我需要根据用户请求用一系列对象填充领域。为了填充领域,我进行了网络调用,然后将对象写入领域。写入完成后,后台线程调用回调,切换到主线程处理结果。我更愿意使用领域通知,但这是一个非常具体的用例,通知不是一个选项。
我无法分享我的完整项目,但我能够通过示例项目重现此问题。这是我的数据模型。
class Owner: Object {
@objc dynamic var uuid: String = ""
@objc dynamic var name: String = ""
convenience init(uuid: String, name: String) {
self.init()
self.uuid = uuid
self.name = name
}
func fetchDogs() -> Results<Dog>? {
return realm?.objects(Dog.self).filter("ownerID == %@", uuid)
}
override class func primaryKey() -> String? {
return "uuid"
}
}
class Dog: Object {
@objc dynamic var uuid: String = ""
@objc dynamic var name: String = ""
@objc dynamic var ownerID: String = ""
func fetchOwner() -> Owner? {
return realm?.object(ofType: Owner.self, forPrimaryKey: ownerID)
}
convenience init(uuid: String, name: String, ownerID: String) {
self.init()
self.uuid = uuid
self.name = name
self.ownerID = ownerID
}
override class func primaryKey() -> String? {
return "uuid"
}
}
我意识到 List
适合 Owner
-> Dog
关系,但在这个项目中这不是一个选项。
示例项目中有两个我们关心的按钮。一种是将一些 Dog
对象添加到领域并将它们与 Owner
UUID 相关联。另一个按钮删除属于所有者的所有 Dog
对象。
基本流程是这样的:
- 删除主人的所有狗
- 在后台线程上创建一组新的狗并将它们添加到领域。
- 调用回调
- 切换回主线程
- 打印主人的所有狗
狗十有八九打印成功。但是,有时 realm 表示没有与当前所有者关联的狗。这可能是什么原因?我的视图控制器逻辑如下。
编辑:我最后要提的是这个问题只能在物理设备上重现。我无法在模拟器上重现它。这让我想知道这是否是一个领域错误。
// MARK: - Props and view controller life cycle
class ViewController: UIViewController {
var owner: Owner?
let ownerUUID = UUID().uuidString
override func viewDidLoad() {
super.viewDidLoad()
owner = makeOwner()
}
}
// MARK: - Action methods
extension ViewController {
@IBAction func onDeletePressed(_ sender: UIButton) {
print("Deleting...")
deleteDogs()
}
@IBAction func onRefreshPressed(_ sender: UIButton) {
print("Creating...")
createDogs {
DispatchQueue.main.async {
self.printDogs()
}
}
}
@IBAction func onPrintPressed(_ sender: UIButton) {
printDogs()
}
}
// MARK: - Helper methods
extension ViewController {
private func makeOwner() -> Owner? {
let realm = try! Realm()
let owner = Owner(uuid: ownerUUID, name: "Bob")
try! realm.write {
realm.add(owner)
}
return owner
}
private func deleteDogs() {
guard let dogs = owner?.fetchDogs() else { return }
let realm = try! Realm()
try! realm.write {
realm.delete(dogs)
}
}
private func createDogs(completion: @escaping () -> Void) {
DispatchQueue(label: "create.dogs.background").async {
autoreleasepool {
let realm = try! Realm()
let dogs = [
Dog(uuid: UUID().uuidString, name: "Fido", ownerID: self.ownerUUID),
Dog(uuid: UUID().uuidString, name: "Billy", ownerID: self.ownerUUID),
Dog(uuid: UUID().uuidString, name: "Sally", ownerID: self.ownerUUID),
]
try! realm.write {
realm.add(dogs)
}
}
completion()
}
}
private func printDogs() {
guard let dogs = owner?.fetchDogs() else { return }
print("Dogs: \(dogs)")
}
}
狗似乎是在单独的线程上创建的; 'background thread' 并且因此它不会有 运行 循环 - 所以两个线程正在查看数据的不同状态。
在没有 运行 循环的线程上创建对象时,必须手动调用 Realm.refresh() 才能将事务推进到最新状态。
上的文档