CoreData中访问托管对象的NSSet关系属性导致崩溃的解决

版权声明:大熊猫猪·侯佩原创或翻译作品.谢绝转载! hopy https://blog.csdn.net/mydo/article/details/85482963

本文介绍了再少数情况下,当访问CoreData托管对象中的NSSet关系属性引起App崩溃的现象以及解决.

现象

在访问托管对象的traces关系属性时,App崩溃,提示:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSMutableSet unionSet:]: set argument is not an NSSet'

traces的类型是NSSet.

实际的代码为:

if let ary = traces?.allObjects as? [HabitTrace]{
    return ary
}
return []

我们再回溯一下崩溃时堆栈里值得注意的的信息:

0   CoreFoundation                      	0x00000001105611bb __exceptionPreprocess + 331
	1   libobjc.A.dylib                     0x000000010f49a735 objc_exception_throw + 48
	2   CoreFoundation                      0x00000001104ad4ec _CFThrowFormattedException + 194
	3   CoreFoundation                      0x00000001105499f5 -[NSMutableSet unionSet:] + 581
	4   CoreData                            0x000000010ff1f3c6 -[_NSFaultingMutableSet willReadWithContents:] + 934
	5   CoreData                            0x000000010fef62db -[_NSFaultingMutableSet allObjects] + 27
	6   LightHabit                          0x000000010d81af8d $S10LightHabit0B0C8traceArySayAA0B5TraceCGvg + 141

注意第5行,实际访问的是_NSFaultingMutableSet中的allObjects属性,而我代码里应该访问的却是NSSet中的allObjects属性.

以上崩溃只在托管对象第一次创建后,立即访问其traces属性时发生,如果之前访问过其他属性则不会有任何问题.

Faulting机制

CoreData有一个Faulting机制:

Faulting

Managed objects typically represent data held in a persistent store. In some situations a managed object may be a “fault”—an object whose property values have not yet been loaded from the external data store—see Faulting and Uniquing for more details. When you access persistent property values, the fault “fires” and the data is retrieved from the store automatically. This can be a comparatively expensive process (potentially requiring a round trip to the persistent store), and you may wish to avoid unnecessarily firing a fault.

大致意思是,为了避免从持久存储读取数据的昂贵操作,CoreData一般会将某些属性设置为fault对象,只有当首次读取该对象发生失效时才真正触发(fires)从持久存储的读取操作!

但不知为何,上面的fire显然没有触发真正的读取,反而导致App崩溃了.

PS:我的读取操作没有跨线程!!!

一种解决方案

为了触发Fault对象的实际读取操作,我们只需要在真正使用该对象前先访问一下托管对象本身就可以了,这相当于一个touch操作:

let managedObject = item.mangedObject
managedObject.traces

如上,我们用一个临时的本地managedObject来做缓存而不是直接访问item.managedObject对象,Faulting机制得以顺利激活,崩溃也不会再发生.

这样我们就解决了该问题.

没有更多推荐了,返回首页

私密
私密原因:
请选择设置私密原因
  • 广告
  • 抄袭
  • 版权
  • 政治
  • 色情
  • 无意义
  • 其他
其他原因:
120
出错啦
系统繁忙,请稍后再试