从后台获取NSObjects时应用崩溃

时间:2018-10-05 21:00:44

标签: swift core-data grand-central-dispatch nsnotificationcenter nsfetchrequest

我收到一个带有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
}

我还没有找到重复此崩溃的方法,我只是在苹果崩溃报告中得到了它。这是回溯:

enter image description here

Book绝对是实体,被命名为Book,所以我知道这不是问题。另外,我对为什么它不被抓住而不是崩溃感到困惑。

注意:该应用程序适用于iOS 9及更高版本,这就是为什么我不使用NSPersistentContainer的原因。

2 个答案:

答案 0 :(得分:1)

查看Apple文档中的this page。您面临的问题是由于您自己创建了后台队列,而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
        }
    }
}

答案 1 :(得分:1)

像这样手动检查线程以确定要返回的上下文不是一个好主意。

呼叫者应该已经知道它需要什么上下文,因此只需提出要求。复制PersistentContainer方法并使用viewContextnewBackgroundContext方法来返回该值。

当您最终放弃iOS 9时,您的迁移路径也会更容易。

-

此外,也无需手动调度到后台线程,私有上下文已经具有自己的队列,可以在其上执行.perform {}块。