从后台获取 NSObjects 时应用程序崩溃

App crashing when fetching NSObjects from background

我有一个 NotificationCenter.default 的通知触发了一个修剪方法,当试图从我的商店中获取时该方法崩溃了。这是方法:

@objc fileprivate func pruneBooks() {
    DispatchQueue.global(qos: .background).async {

        let context = cdStack.getManagedObjectContext()
        context.perform {
            do{
                let request = NSFetchRequest<Book>(entityName: "Book")
                let result = try context.fetch(request)\ <----- CRASHES HERE
                //DO STUFF
            }catch{
                // Handle Error
            }
        }
    }
}

这就是我的 getManagedObjectContext 方法的样子:

func getManagedObjectContext() -> NSManagedObjectContext {


    let thread = Thread.current

    if thread.isMainThread {
        return mainMOC
    }

    let childContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
    childContext.parent = mainMOC

    NotificationCenter.default.addObserver(self,
                                           selector: #selector(didReceiveChildContextDidSave(notification:)),
                                           name: .NSManagedObjectContextDidSave,
                                           object: childContext)

    return childContext
}

我还没有找到重现此崩溃的方法,我只是在 Apple 崩溃报告中找到它。这是回溯:

Book 绝对是实体,被命名为 Book,所以我知道这不是问题所在。另外,我很困惑为什么它不会捕获而不是崩溃。

注意:该应用程序适用于 iOS 9 岁及以上,这就是我不使用 NSPersistentContainer.

的原因

查看 this page Apple 文档。您面临的问题是您自己创建后台队列,而 ManagedObjectContext 有自己的后台队列。可以通过 perform 方法访问此队列,该方法在该队列上(因此在后台)执行您的块。如果我是正确的,你应该删除你的 pruneBooks 函数的第一行,这样它看起来像这样:

@objc fileprivate func pruneBooks() {
    let context = cdStack.getManagedObjectContext()
    context.perform {
        do{
            let request = NSFetchRequest<Book>(entityName: "Book")
            let result = try context.fetch(request)\ <----- CRASHES HERE
            //DO STUFF
        }catch{
            // Handle Error
        }
    }
}

像这样手动检查线程来决定将什么上下文 return 不是一个好主意。

调用者应该已经知道它需要什么上下文,所以直接询问即可。复制 PersistentContainer 方法并具有 viewContextnewBackgroundContext 方法,return 完全相同。

当你最终放弃 iOS 9.

时,你的迁移路径也会更容易

-

也不需要手动分派到后台线程,私有上下文已经有自己的队列,它将在其上执行 .perform {} 块。