iOS Core Data父上下文删除对象后创建子上下文时删除状态未同步问题
Core Data 父子上下文未保存删除状态不同步问题说明
这不是操作失误,也不是Core Data的bug,是其上下文变更追踪机制的既定设计,核心原因如下:
- 新建子上下文不会在初始化时全量同步父上下文内存中所有未保存的临时变更,它的初始状态锚点是父上下文上一次成功保存后,持久化存储层留存的对象快照。
- 新增、属性更新类的变更,在子上下文通过
object(with:)获取对象、或者执行fetch请求触发fault fire时,父上下文会把对应对象的最新属性值透传给子上下文,因此这两类变更看起来可以被新建的子上下文感知。 isDeleted删除标记属于上下文独有的内存临时状态,不会在未保存的状态下跨上下文传递:父上下文执行delete后,仅会在自身内存中给对象打上删除标记,这个标记不会同步给后续新建的子上下文。子上下文通过objectID拉取对象时,只会从持久化层读取到该对象尚未删除的原始状态,因此拿到的对象isDeleted属性为false。
认知偏差提示:子上下文创建完成后不会自动和父上下文的内存临时状态完全对齐,所有未保存到持久化层(或父上下文的上一层存储锚点)的删除操作,都不会被新建子上下文自动感知。
可行解决方案
- 方案1:如果业务场景允许,在父上下文执行删除操作后先调用
parentContext.save()将变更提交,之后再创建子上下文,此时子上下文拉取对象时就会正确感知到对象已被删除的状态。 - 方案2:如果不希望提前保存父上下文的变更,可以在创建子上下文后,监听父上下文的
NSManagedObjectContextObjectsDidChange通知,拿到父上下文标记为删除的对象ID列表,手动在子上下文中对对应对象执行删除操作,手动对齐状态。 - 注意:仅调用
parentContext.processPendingChanges()只能清空父上下文自身的挂起变更队列,无法让未保存的删除标记传递给后续新建的子上下文,不能解决这个问题。
问题复现代码
func testDelete(someObject: NSManagedObject) { guard let parentContext = someObject.managedObjectContext else { return } let objectID = someObject.objectID parentContext.performAndWait { parentContext.delete(someObject) print("Parent isDeleted: \(someObject.isDeleted)") // 输出: "Parent isDeleted: true" } let childContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType) childContext.parent = parentContext childContext.performAndWait { let childObject = childContext.object(with: objectID) print("Child isDeleted: \(childObject.isDeleted)") // 输出: "Child isDeleted: false" } }
内容的提问来源于stack exchange,提问作者ChipsAndBits
相关产品推荐
相关产品推荐

