为什么在调用“privateManagedObjectContext.perform”时会崩溃(从 com.apple.main-thread (Thread 1) Enqueued)?

Mic*_*nna 5 core-data ios swift

在关闭网络请求时,我使用私有并发队列将对象插入到核心数据中,并在私有上下文中调用“执行”时发生崩溃。

控制台中的崩溃消息:

libc++abi.dylib:以未捕获的 NSException 类型异常终止

堆栈跟踪: 在此处输入图片说明

导致崩溃的代码:

API.sync(onlyMe, syncToken: syncToken) { success, syncResponse in
    CoreDataUtils.privateContext.perform { // crashes on this line
        ....
    }
}
Run Code Online (Sandbox Code Playgroud)

我的核心数据堆栈(不幸的是,目前位于 AppDelegate 而不是 CoreDataStack 类中):

lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = {
    // The persistent store coordinator for the application. This implementation creates and returns a coordinator, having added the store for the application to it. This property is optional since there are legitimate error conditions that could cause the creation of the store to fail.
    // Create the coordinator and store
    var coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
    let url = self.applicationDocumentsDirectory.appendingPathComponent("SingleViewCoreData.sqlite")
    let options = [NSMigratePersistentStoresAutomaticallyOption: true,
                    NSInferMappingModelAutomaticallyOption: true]
    do {
        try coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: url, options: options)
    } catch {
        print(error)
    }

    return coordinator
}()

lazy var privateManagedObjectContext: NSManagedObjectContext = {
    // Initialize Managed Object Context
    var managedObjectContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)

    // Configure Managed Object Context
    managedObjectContext.parent = self.managedObjectContext

    return managedObjectContext
}()

lazy var managedObjectContext: NSManagedObjectContext = {
    // Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.) This property is optional since there are legitimate error conditions that could cause the creation of the context to fail.
    var managedObjectContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
    managedObjectContext.persistentStoreCoordinator = self.persistentStoreCoordinator
            managedObjectContext.mergePolicy = NSRollbackMergePolicy //This policy discards in-memory state changes for objects in conflict. The persistent store’s version of the objects’ state is used

    return managedObjectContext
}()
Run Code Online (Sandbox Code Playgroud)

CoreDataUtils.privateContext 在哪里:

class CoreDataUtils: NSObject {

    static let appDel = UIApplication.shared.delegate as! AppDelegate
    static let context: NSManagedObjectContext { 
        return appDel.managedObjectContext 
    }
    static let privateContext: NSManagedObjectContext {   
        appDel.privateManagedObjectContext 
    }
}
Run Code Online (Sandbox Code Playgroud)

Mic*_*nna 1

我按照 @CodeBender 的链接发现有几个位置出现多线程违规。该链接提供了一种通过在项目方案中传递参数来调试并发问题的方法。此方法提供了有关问题所在和位置的更详细信息。

例如,我有一个执行提取的函数,但我没有将代码包装在perform块中。这就是我实施修复的方法:

static func searchObject(in context: NSManagedObjectContext = context, _ entity: String, key: String, value: Any) -> [NSManagedObject]? {

    var objects: [NSManagedObject]?

    context.performAndWait {
        //searching core data for a specific attribute within an identity via a predicate
        let request = NSFetchRequest<NSFetchRequestResult>(entityName: "\(entity)")
        request.returnsObjectsAsFaults = false
        request.predicate = NSPredicate(format: "\(key) == %@", "\(value)")

        do {
            let results = try context.fetch(request)
            objects = results as? [NSManagedObject]
        } catch {
            let nserror = error as NSError
            Bugsnag.notifyError(nserror)
            NSLog("Unresolved error \(nserror), \(nserror.userInfo)")
            abort()
        }
    }


    return objects
}
Run Code Online (Sandbox Code Playgroud)

Notification另外,在我从服务器插入对象的另一个地方,我正在调用该perform块,但我没有使用正确的上下文实例化托管对象。

CoreDataUtils.privateContext.perform {
    for notification in notifications {

        // BEFORE (WRONG) - would default to the main context (CoreDataUtils.context - see question)
        //notification.insert()

        // NOW (CORRECT) - inserts into private queue we are performing on
        notification.insert(into: CoreDataUtils.privateContext)
    }

    CoreDataUtils.save()
}
Run Code Online (Sandbox Code Playgroud)

Notification我在模型中的位置:

func insert(into context: NSManagedObjectContext = CoreDataUtils.context) -> NotificationObject {
    assert(CoreDataUtils.searchObject(Models.notification, key: "id", value: self.id) == nil)
    let newNotification = NotificationObject(notification: self, context: managedObjectContext)
    return newNotification
}
Run Code Online (Sandbox Code Playgroud)