Ios 从错误线程访问的域-Swift 3

Ios 从错误线程访问的域-Swift 3,ios,swift,multithreading,realm,grand-central-dispatch,Ios,Swift,Multithreading,Realm,Grand Central Dispatch,myUITableViewController的顶部有以下内容: let queue = DispatchQueue(label: "background") 删除任务时,将执行以下操作: self.queue.async { autoreleasepool { let realm = try! Realm() realm.beginWrite() realm.delete(task) do { t

my
UITableViewController
的顶部有以下内容:

let queue = DispatchQueue(label: "background")
删除任务时,将执行以下操作:

self.queue.async {
    autoreleasepool {
        let realm = try! Realm()
        realm.beginWrite()
        realm.delete(task)
        do {
            try realm.commitWrite()
        } catch let error {
            self.presentError()
        }
    } 
 }
然后我收到了错误

以类型为的未捕获异常终止 realm::IncorrectThreadException:从错误线程访问的领域


如何修复此问题?

似乎写入发生在与最初访问对象不同的线程上。您应该能够通过传递
任务
的id来修复它,并在写入之前(在异步块内)使用该id从数据库中获取它

因此,在顶部:

var taskId = 0  // Set this accordingly
然后像

self.queue.async {
    autoreleasepool {
        let realm = try! Realm()
        let tempTask = // get task from Realm based on taskId
        realm.beginWrite()
        realm.delete(tempTask)
        do {
            try realm.commitWrite()
        } catch let error {
            self.presentError()
        }
    } 
 }

我们需要了解不能从不同线程访问领域对象的事实。这意味着什么以及如何解决这个问题

首先,领域对象不能从不同的线程访问意味着,一个线程中定义的领域实例不能从不同的线程访问。我们实际上应该做的是,我们需要为每个线程提供不同的领域实例实例

例如,让我们看看下面的例子,我们在后台线程中异步插入50条记录,点击按钮,然后在主线程中添加通知块,以更新计数标签中的人数。每个线程(主线程和后台线程)都有自己的领域对象实例来访问领域数据库。因为领域数据库通过限制领域线程实例来实现线程安全

class Person: Object {
    dynamic var name = ""
    convenience init(_ name: String) {
        self.init()
        self.name = name
    }
}


override func viewDidAppear(_ animated: Bool) {
    let realmMain = try!  Realm ()
    self.people = realmMain.objects(Person.self)
    self.notification = self.people?.addNotificationBlock{ [weak self] changes in
        print("UI update needed")
        guard let countLabel = self?.countLabel else {
            return
        }
        countLabel.text = "Total People: \(String(describing: self?.people?.count))"
    }
}

@IBAction func addHandler(_ sender: Any) {
    print(#function)
    let backgroundQueue = DispatchQueue(label: "com.app.queue",
                                        qos: .background,
                                        target: nil)



    backgroundQueue.async {
        print("Dispatched to background queue")
        let realm = try! Realm()
        try! realm.write {
            for i in 1..<50 {
                let name = String(format: "rajan-%d", i)
                //print(#function, name)
                realm.add(Person(name))
            }
        }

    }
}
类人物:对象{
动态变量名称=“”
便利初始化(u名称:String){
self.init()
self.name=名称
}
}
覆盖函数视图显示(u动画:Bool){
让realmMain=try!Realm()
self.people=realmMain.objects(Person.self)
self.notification=self.people?.addNotificationBlock{[weak self]中的更改
打印(“需要UI更新”)
guard let countLabel=self?.countLabel else{
返回
}
countLabel.text=“总人数:\(字符串(描述:self?.People?.count))”
}
}
@iAction func addHandler(\发送方:任意){
打印(#功能)
让backgroundQueue=DispatchQueue(标签:“com.app.queue”,
服务质量:。背景,
目标:零)
backgroundQueue.async{
打印(“发送到后台队列”)
让realm=try!realm()
试试看!写吧{

对于1..中的i,您还可以使用ThreadSafe引用,这是在线程之间传递领域对象的一种特定方式:

let realm = try! Realm()
let person = Person(name: "Jane") // no primary key required 
try! realm.write {
    realm.add(person)
}
let personRef = ThreadSafeReference(to: person)
DispatchQueue(label: "com.example.myApp.bg").async {
    let realm = try! Realm()
    guard let person = realm.resolve(personRef) else {
    return // person was deleted
}
try! realm.write {
    person.name = "Jane Doe"
}
Realm文档提供的步骤包括:

  • 使用线程受限对象初始化ThreadSafeReference
  • 将该ThreadSafeReference传递到目标线程或队列
  • 通过调用 Realm.resolve(\:)
  • 像平常一样使用返回的对象
  • 你也可以

    Realm accessed from incorrect thread
    

    如果您试图通过获取的项写入,那么我无法从主线程(UI线程)读取如果我想写?我应该重新考虑使用线程吗?是的,当对象在线程之间传递时,领域会崩溃。这在我身上发生过很多次:)如果我还必须进行额外的读取,你认为线程化写有什么好处吗?谢谢!这就是我们建议卸载的方式工作到后台线程。您可以将
    taskId
    设置为域对象的主键,然后使用
    Realm.object(ofType:forPrimaryKey:)在后台线程上检索对它的引用
    。根据一般经验,我们建议尽可能将持久的领域写入事务卸载到后台线程。在写入过程中,可以在其他线程上继续从领域读取,但需要注意的是,如果在后台正在进行写入,而您尝试在主线程上启动一个,则可能会导致错误检查UI,直到后台操作完成。