合并Core Data上下文后NSFetchedResultsController返回重复对象问题咨询
问题原因
这是Core Data上下文自动合并+临时ID转永久ID时序差触发的NSFetchedResultsController(下称FRC)已知异常,核心触发逻辑如下:
- 新增Item对象时,Core Data会先给对象分配临时ID,此时
viewContext.processPendingChanges()会让绑定viewContext的FRC感知到新增对象,插入第一条记录。 - 保存
viewContext时,变更推送到父上下文rootCtx,此时对象仍为临时ID,若FRC触发了第二次变更通知,会误判为新对象插入第二条记录。 - 保存
rootCtx到持久化存储时,对象才会被分配永久ID,由于你给viewContext开启了automaticallyMergesChangesFromParent,rootCtx的变更会自动合并回viewContext,部分场景下会再次触发FRC的插入逻辑。 - 额外的无效配置加剧了问题:rootCtx直接绑定持久化存储协调器,没有父上下文,你给它开启
automaticallyMergesChangesFromParent完全无意义,反而会引发不必要的变更通知。
解决方案
按优先级从高到低尝试以下方案:
- 新增对象保存前先获取永久ID,避免临时ID转永久ID的时序问题
func weirdTest() { print("1. Adding.") let item = Item(context: viewContext) item.id = UUID() // 新增这行,提前获取永久ID try! viewContext.obtainPermanentIDs(for: [item]) viewContext.processPendingChanges() print("2. Saving.") viewContext.performAndWait { try! viewContext.save() } rootCtx.performAndWait { try! rootCtx.save() } }
- 删除rootCtx的无效自动合并配置
lazy var rootCtx: NSManagedObjectContext = { let rootCtx = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType) rootCtx.persistentStoreCoordinator = coordinator // 移除该行无效配置 // rootCtx.automaticallyMergesChangesFromParent = true return rootCtx }()
- 若仍有异常,手动对FRC返回的结果去重
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) { let rawItems = (controller.fetchedObjects ?? []) as! [Item] // 按objectID去重,避免重复条目 items = rawItems.reduce(into: []) { result, item in if !result.map({ $0.objectID }).contains(item.objectID) { result.append(item) } } }
内容的提问来源于stack exchange,提问作者msmialko
相关产品推荐
相关产品推荐

