无法让NSManagedObject.automaticallyMergesChangesFromParent起作用

4

我已经重写了我们的iOS应用程序,使用Core Data中的新iOS 10功能。特别是,我已经将Core Data堆栈创建切换到新的NSPersistentStore设置。

我们已经有了一个主队列上下文和一个后台上下文,所以我们直接从NSPersistentStore对象使用它们。我还从使用通知来合并跨上下文的更改,改为在viewContext和backgroundContext中设置新的automaticallyMergesChangesFromParent。

不幸的是,现在我们的NSFetchedResultsController中出现了一些奇怪的崩溃,它更新了UICollectionViewController和视图。

因此,我设置了“-com.apple.CoreData.ConcurrencyDebug 1”启动参数,以确保事情发生在正确的队列上,但当后台线程尝试保存后台对象上下文时,我立即得到一个异常,尽管堆栈跟踪显示异常发生在“正确”的后台队列上:

#0  0x00000001948b89bc in +[NSManagedObjectContext __Multithreading_Violation_AllThatIsLeftToUsIsHonor__] ()
#1  0x0000000194853724 in -[_PFBatchFaultingArray managedObjectIDAtIndex:] ()
#2  0x0000000194853600 in -[_PFMutableProxyArray newArrayFromObjectIDs] ()
#3  0x0000000194853548 in -[_PFMutableProxyArray arrayFromObjectIDs] ()
#4  0x0000000194852638 in -[NSFetchedResultsController(PrivateMethods) _managedObjectContextDidSave:] ()
#5  0x00000001924e622c in __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ ()
#6  0x00000001924e5930 in _CFXRegistrationPost ()
#7  0x00000001924e56ac in ___CFXNotificationPost_block_invoke ()
#8  0x0000000192554b9c in -[_CFXNotificationRegistrar find:object:observer:enumerator:] ()
#9  0x0000000192427bf4 in _CFXNotificationPost ()
#10 0x0000000192f2e6bc in -[NSNotificationCenter postNotificationName:object:userInfo:] ()
#11 0x00000001948c5b48 in -[NSManagedObjectContext(_NSInternalNotificationHandling) _postContextDidSaveNotificationWithUserInfo:] ()
#12 0x0000000194850350 in -[NSManagedObjectContext(_NSInternalAdditions) _didSaveChanges] ()
#13 0x000000019483ca0c in -[NSManagedObjectContext save:] ()

你有没有想法,我可能做错了什么?以下是相关代码片段:

var initializationError: NSError?

// The persistent container (local database) for the application.
lazy var persistentContainer: NSPersistentContainer = {

    let container = NSPersistentContainer(name: self.storeName)
    container.loadPersistentStores(completionHandler: { (storeDescription, error) in
        if let error = error as NSError? {
            self.initializationError = error // This will be displayed by the AppDelegate
        }
    })
    return container
}()

// The managed object context for the main thread of the application.
lazy var mainObjectContext: NSManagedObjectContext? = {

    // Get the main thread context.
    let context = self.persistentContainer.viewContext
    context.mergePolicy = NSOverwriteMergePolicy // In-memory properties overwrite datastore properties
    context.automaticallyMergesChangesFromParent = true // Merge all changes.

    return context
}()

// Return a managed object for background threads to create and modify objects without affecting the main thread until necessary.
lazy var backgroundObjectContext: NSManagedObjectContext? = {

    // Get a background thread context.
    let context = self.persistentContainer.newBackgroundContext()
    context.mergePolicy = NSOverwriteMergePolicy // In-memory properties overwrite datastore properties
    context.automaticallyMergesChangesFromParent = true // Merge all changes.

    return context

}()

// Synchronously save any pending object changes in a managed object context to the database persistent store.
class func saveAnyChangesSync(_ context: NSManagedObjectContext) -> NSError? {

    var error: NSError?

    // If the context has changes, save them.
    if context.hasChanges {

        do {
            try context.save()

        } catch let saveError as NSError {
            error = saveError
            NSLog("CoreData error saving context \(saveError)")
        }
    }
    return error
}

你确定它是使用子上下文在内部实现的吗?在WWDC上,CD开发人员告诉我们,随着新的内部更改,不再建议使用子/父模式。 - Léo Natan
你的应用程序中是否正确使用了背景和视图上下文? - Léo Natan
2个回答

2
如果您正在使用Swift管理核心数据,但未看到后台保存和主要上下文之间传播更改,则可能会错过autoreleasepool部分(噗!Objective-C)。在进行此更改之前,我看到了一堆错误,尽管我的错误都是关于NSSet被改变的。
您还可以查看合并策略-我认为两个上下文不应具有相同的策略。主要应该让位于文件存储,后台应该坚持当前对象更改。
缩写示例:
// When initializing:
persistentContainer.viewContext.automaticallyMergesChangesFromParent = true
 persistentContainer.viewContext.mergePolicy = NSMergePolicy.mergeByPropertyStoreTrump
// And when saving:
persistentContainer.performBackgroundTask { moc in
    moc.automaticallyMergesChangesFromParent = true
    moc.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
    autoreleasepool {
        // ... do some nsmanagedobject loading/changing
    }
    do {
        try moc.save()
    } catch {}
}

关于不同上下文中的 .mergePolicy 的好提示,谢谢!同时,我认为对于临时后台上下文来说 moc.automaticallyMergesChangesFromParent = true 是多余的,因为您只为一个特定任务创建它。 - Legonaftik

1
我猜测saveAnyChangesSync是从主线程调用的,使用了后台上下文,在这种情况下,您需要使用上下文的执行方法。
/* asynchronously performs the block on the context's queue.  Encapsulates an autorelease pool and a call to processPendingChanges */
- (void)performBlock:(void (^)(void))block API_AVAILABLE(macosx(10.7),ios(5.0));

/* synchronously performs the block on the context's queue.  May safely be called reentrantly.  */
- (void)performBlockAndWait:(void (NS_NOESCAPE ^)(void))block API_AVAILABLE(macosx(10.7),ios(5.0));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接