在NSManagedObjectContext的executeAndWait块中,从当前线程对主线程执行选择器。

问题描述 投票:0回答:1

我有一个 NSManagedObjectContext 的初始化,它是由 newBackgroundContextpersistentContainer 如下。

managedContext = coreDataStack.persistentContainer.newBackgroundContext()

是这样的 persistentContainer 看起来像。

lazy var persistentContainer: NSPersistentContainer = {
    let container = NSPersistentContainer(name: "myXCDataModelName")
    container.loadPersistentStores(completionHandler: { [weak self] (_, error) in
        if let self = self,
            let error = error as NSError? {
            print("error!")
        }
    })
    return container
}()

我在用 newBackgroundContext 的任何CRUD操作,以确保 CoreData 可以安全地完成,无论哪个线程试图在其上进行修改。managedContext而不是确保或强制每个操作都在主线上完成。

我有一个 saveContext 方法,我试图对 managedContext 里面 performAndWait 块如下。

managedContext.performAndWait {
    do {
        guard UIApplication.shared.isProtectedDataAvailable,
            managedContext.hasChanges else {
            return
        }
        try managedContext.save()
    } catch {
        print("error!")
    }
}

它看起来像 performAndWait 大部分时间是在主线程上运行,但当它在另一个线程上运行时,线程检查器会因以下检查而产生警告。UIApplication.shared.isProtectedDataAvailable 因为它应该在主线程上完成。

我决定在主线程上运行一个选择器来进行这个检查,所以我声明了一个叫做 isProtectedDataAvailable Bool,在类级将其默认为false,然后在这个选择器运行时更新其值。

@objc private func checker() {
     isProtectedDataAvailable = UIApplication.shared.isProtectedDataAvailable
}

现在我重构了 performAndWait 块,以便在主线程上运行检查,如果它被其他线程调用。

managedContext.performAndWait {
    do {        
        if Thread.isMainThread {
            checker()
        } else {
            print("RUNNING ON ANOTHER THREAD")
            Thread.current.perform(#selector(checker),
                                   on: Thread.main,
                                   with: nil,
                                   waitUntilDone: true,
                                   modes: nil)
        }

        guard isProtectedDataAvailable,
            managedContext.hasChanges else {
            return
        }
        try managedContext.save()
    } catch {
        print("error!")
    }
}

当我在模拟器或真实设备上运行它时,它似乎工作得很好,我产生了不同的核心数据相关操作,这些操作将触发主线程和后台线程上的保存上下文。

但是发生的情况是,如果我把一些断点放进 performAndWait 块,并停止执行,以检查代码块的工作情况,它有时会导致应用程序冻结,当我继续执行,就像一个死锁发生。我不知道这是否与使用断点停止执行有某种关系,还是我的实现出了问题,尽管没有断点也能正常工作,没有出现应用冻结之类的情况。

我很担心,因为在使用这个解决方案之前,我尝试过在主线程上同步,灵感来自于以下内容 回答 只需切换到主线程进行检查,如下图所示(导致应用程序冻结,我认为是死锁),在里面的 performAndWait 块。

var isProtectedDataAvailable = false

if Thread.isMainThread {
    isProtectedDataAvailable = UIApplication.shared.isProtectedDataAvailable
} else {
    DispatchQueue.main.sync {
        isProtectedDataAvailable = UIApplication.shared.isProtectedDataAvailable
    }
}

我不得不用 sync 而不是 async的更新值,因为我必须检索更新后的 isProtectedDataAvailable 在继续执行之前。

有什么想法吗?

ios swift multithreading core-data nsmanagedobjectcontext
1个回答
1
投票

我想你的问题是关于正确的方法,这样代码就能正常工作,并且允许设置任何断点而不陷入死锁。)

为什么不试试这种方式(从你所说的,我看不出有什么理由反对它)。

  1. 首先评估 UIApplication.shared.isProtectedDataAvailable 在主线上。(我猜想,还有更复杂的条件,不仅要满足 UIApplication.shared.isProtectedDataAvailable = true但为了保持简单...)
  2. 如果 UIApplication.shared.isProtectedDataAvailable (和 其他条件)评价为 true 继续在后台线程上进行数据处理和保存。由于 managedContext 是作为一个 newBackgroundContext 它可以在那里使用。

主线上的说明

    if UIApplication.shared.isProtectedDataAvailable && otherConditions {
        DispatchQueue.global(qos: .userInitiated).async {
            dataProcessing()
        }
    }

关于背景线的说明

    static func dataProcessing() {
       // get the managedContext
       let context = AppDelegate.appDelegate.managedContext
       context.performAndWait {
          if context.hasChanges {
             do {             
                try context.save()
             } catch {
                print("error!")
             }
          }
       }
    }

0
投票

出于某种原因,将下面的检查移动到主队列之外,并与主队列同步。performAndWait 解决了所有问题。

无论是有断点还是没有断点,都不会发生死锁或应用程序冻结,无论哪个线程触发了包含有 performAndWait

所以现在的方法体是这样的。

var isProtectedDataAvailable = false
if Thread.isMainThread {
    isProtectedDataAvailable = UIApplication.shared.isProtectedDataAvailable
} else {
    DispatchQueue.main.sync {
        isProtectedDataAvailable = UIApplication.shared.isProtectedDataAvailable
    }
}

managedContext.performAndWait {
    do {
        guard isProtectedDataAvailable,
        managedContext.hasChanges else {
            return
        }
        try managedContext.save()
    } catch {
        print("error")
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.