@NSManaged 属性是否线程安全?对应的最佳实践有哪些?
@NSManaged属性线程安全问题解答
给定场景的线程安全表现
你给出的代码存在明确的线程安全风险,轻则出现属性取值错乱、脏数据持久化问题,重则直接触发Core Data的运行时断言崩溃。
核心原因是Core Data的NSManagedObject、NSManagedObjectContext(下文简称MOC)都遵循严格的线程绑定规则:只能在自身所属MOC对应的队列上访问属性、执行操作,任何跨队列/跨上下文访问Managed Object的行为都违反Core Data并发约定。
这段代码的具体隐患点:self作为NSManagedObject实例是在外部队列被捕获到perform闭包中的,直接访问self.myProperty时,没有校验self是否绑定到当前执行perform的concurrentNSManagedObjectContext,也没有在闭包内通过当前上下文重新获取对象,本质属于非法跨队列访问属性。
对应使用最佳实践
- 永远不要直接在
perform/performAndWait闭包中捕获外部的NSManagedObject实例,优先捕获线程安全的NSManagedObjectID,在闭包内部通过当前执行操作的MOC重新获取对象后再访问属性,正确写法参考:
@NSManaged public var myProperty: SomeType func someFunc() { // 捕获线程安全的唯一标识,不受队列/上下文限制 let targetObjectID = self.objectID concurrentNSManagedObjectContext.perform { // 在当前MOC的队列内重新获取对象,保证访问安全 guard let target = try? concurrentNSManagedObjectContext.existingObject(with: targetObjectID) as? YourManagedObjectSubclass else { return } doSomething(with: target.myProperty) } }
- 开发阶段开启Core Data并发调试开关:在Xcode Scheme的Run-Arguments中添加启动参数
-com.apple.CoreData.ConcurrencyDebug 1,只要违反并发规则就会直接触发断点,提前暴露问题。 - 跨上下文传递数据时,只允许传递
NSManagedObjectID,禁止直接传递NSManagedObject实例。 - 即使确认捕获的对象和执行
perform的MOC为同一个,也建议在闭包内通过MOC重新获取对象,避免对象提前释放、上下文状态变更带来的异常问题。
内容的提问来源于stack exchange,提问作者aimelda
相关产品推荐
相关产品推荐

