Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/swift/19.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios 线程之间的域数据不一致_Ios_Swift_Realm - Fatal编程技术网

Ios 线程之间的域数据不一致

Ios 线程之间的域数据不一致,ios,swift,realm,Ios,Swift,Realm,我有一个案例,我需要根据用户请求用一系列对象填充领域。为了填充realm,我进行了一个网络调用,然后将对象写入realm。写入完成后,后台线程调用一个回调,该回调切换到主线程以处理结果。我更喜欢使用领域通知,但这是一个非常具体的用例,通知不是一个选项 我不能分享我的完整项目,但我能够用一个示例项目重现这个问题。这是我的数据模型 class Owner: Object { @objc dynamic var uuid: String = "" @objc dynamic var n

我有一个案例,我需要根据用户请求用一系列对象填充领域。为了填充realm,我进行了一个网络调用,然后将对象写入realm。写入完成后,后台线程调用一个回调,该回调切换到主线程以处理结果。我更喜欢使用领域通知,但这是一个非常具体的用例,通知不是一个选项

我不能分享我的完整项目,但我能够用一个示例项目重现这个问题。这是我的数据模型

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"
    }
}

看起来狗是在一个单独的线程上创建的;一个“后台线程”,因此它不会有运行循环——因此这两个线程正在查看数据的不同状态

在没有运行循环的线程上创建对象时,必须手动调用Realm.refresh(),以便将事务推进到最新状态


查看有关两件事的文档。当你说后台线程时,你是指用.qos后台创建的线程吗?与
DispatchQueue.global(qos:.background).async中的一样
?第二件事是这个代码的结构方式,在主人和狗之间根本没有关系。这些是完全独立的对象,它们之间没有图形。因为如果是这样的话,问题就归结为:;我有两套物体。如果我删除一组对象,然后在“后台线程”上创建另一组对象,然后切换回主线程并尝试打印它们,但它们不在那里。另外,
领域
来自owner.fetchDogs中的哪里?您可能需要查看Realm.refresh(),快速查看一下,这些创建的狗似乎与其他数据位于完全不同的线程上。这也是一本很好的读物,可能会增加一些clarity@Jay当我说背景线程时,我只是指主线程以外的线程。而且,你是对的,主人和狗之间没有实际的关系。没有列表,没有链接对象。相反,Dog对象有一个名为ownerID的属性来关联这两个属性。@Jay.fetchDogs中的
领域
属性由
对象
类继承。您是正确的,狗对象是故意在背景线程上创建的。在我的实际项目中,我们发出一个网络请求,将数据存储在一个单独的线程中,然后调用回调。创建的狗和其他数据应该在不同的线程上。@Jay你是对的
Realm.refresh()是一种解决方案。如果你想发布一个解决方案,我会相信你。我发现了另一个解决方案。如果我没有维护对所有者的引用,只要在需要时从域中获取所有者,就不需要刷新。@Rob非常正确,因为该对象将存在于同一线程上。但是,如果您在UI中使用对象,例如tableView,则会增加一些复杂性。
// 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)")
    }
}