无法为CoreData视图上下文设置NSMergePolicy问题排查
CoreData设置视图上下文合并策略时卡顿问题排查
问题描述
能正常看到「Did load CoreData store」日志,但应用在「Step 1」之后、设置视图上下文的merge policy时出现卡顿(尝试其他merge policy也有相同问题)。移除该行代码后,应用可正常启动。
用户代码
private func _setupContainer() { persistentContainer = .init(name: "MyAppName") let storeDescription = persistentContainer.persistentStoreDescriptions.first! storeDescription.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey) persistentContainer.loadPersistentStores { _, error in if let error { fatalError("Failed to load persistent container: \(error)") } Log.info("Did load CoreData store") } Log.debug("Step 1") persistentContainer.viewContext.mergePolicy = NSMergePolicy.overwrite // NSMergeByPropertyObjectTrumpMergePolicy Log.debug("Step 2") mainContext.undoManager = nil mainContext.shouldDeleteInaccessibleFaults = true mainContext.automaticallyMergesChangesFromParent = true Log.debug("Did setup CoreData container") }
问题原因及解决办法
核心原因
persistentContainer.loadPersistentStores执行完成后,viewContext会被自动激活(CoreData默认行为),此时修改mergePolicy会触发上下文对现有所有对象的校验与状态同步,当存储的数据量较大时就会出现明显卡顿。推荐解决方案:提前配置合并策略
将mergePolicy的配置移到loadPersistentStores调用之前,此时视图上下文尚未激活,修改配置不会触发额外的同步操作:
private func _setupContainer() { persistentContainer = .init(name: "MyAppName") // 提前设置视图上下文的合并策略 persistentContainer.viewContext.mergePolicy = NSMergePolicy.overwrite let storeDescription = persistentContainer.persistentStoreDescriptions.first! storeDescription.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey) persistentContainer.loadPersistentStores { _, error in if let error { fatalError("Failed to load persistent container: \(error)") } Log.info("Did load CoreData store") } Log.debug("Step 1") Log.debug("Step 2") mainContext.undoManager = nil mainContext.shouldDeleteInaccessibleFaults = true mainContext.automaticallyMergesChangesFromParent = true Log.debug("Did setup CoreData container") }
- 备选方案:禁用自动激活后延迟配置
如果必须在加载存储后配置,可以先禁用视图上下文的自动激活,完成所有配置后再手动激活:
private func _setupContainer() { persistentContainer = .init(name: "MyAppName") let storeDescription = persistentContainer.persistentStoreDescriptions.first! storeDescription.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey) // 禁用自动激活,避免提前触发上下文同步 persistentContainer.viewContext.automaticallyActivates = false persistentContainer.viewContext.automaticallyMergesChangesFromParent = false persistentContainer.loadPersistentStores { _, error in if let error { fatalError("Failed to load persistent container: \(error)") } Log.info("Did load CoreData store") } Log.debug("Step 1") persistentContainer.viewContext.mergePolicy = NSMergePolicy.overwrite Log.debug("Step 2") // 恢复配置并手动激活上下文 persistentContainer.viewContext.undoManager = nil persistentContainer.viewContext.shouldDeleteInaccessibleFaults = true persistentContainer.viewContext.automaticallyMergesChangesFromParent = true try? persistentContainer.viewContext.setQueryGenerationFrom(.current) persistentContainer.viewContext.automaticallyActivates = true Log.debug("Did setup CoreData container") }
补充说明
- CoreData视图上下文激活后,任何核心配置的变更都会触发对现有托管对象的遍历校验,数据量越大性能开销越显著。
- 优先选择提前配置的方案,从根源避免不必要的同步操作,保证启动性能。
内容的提问来源于stack exchange,提问作者Thomas Albert
相关产品推荐
相关产品推荐

