我的应用程序使用以下函数来序列化一组自定义对象(仅显示相关代码):
class func serializeShoppingLocations(buyLocations: Set<ShoppingLocation>) -> Data {
#if os(iOS)
NSKeyedArchiver.setClassName("ShoppingLocation", for: ShoppingListIOS.ShoppingLocation.self)
#elseif os(watchOS)
NSKeyedArchiver.setClassName("ShoppingLocation", for: ShoppingListWatch.ShoppingLocation.self)
#endif
let data = NSKeyedArchiver.archivedData(withRootObject: buyLocations)
return data
}
要求iOS和watchOS目标的条件编译是为了允许两个目标稍后取消归档序列化数据,即使它们已被其他目标归档。
ShoppingLocation
采用NSCoding
协议,并继承自NSObject
。
此代码在let data = …
指令与EXC_BAD_ACCESS (code=1, address=0x0)
崩溃。
我知道错误可能表示引用了一个不存在的对象。但是,变量buyLocations
是存在的,我可以在调试器中打印该值,包括集合中对象的所有值。
堆栈跟踪指示归档程序开始归档NSSet
,并尝试归档该组的一个元素,但内部可变字典中的setObjectForKey
失败。
可能是什么原因?
编辑:
在进一步测试期间,我意识到当应用程序的至少2个线程处于活动状态时,崩溃总是发生 特别是,我发现一个线程在一个不同于上述指令的指令中崩溃(Xcode中的红色):
let dataBlob = NSKeyedArchiver.archivedData(withRootObject: shoppingItem.buyLocations)
并且另一个线程在上面提到的指令处停止(Xcode中为绿色):
let data = NSKeyedArchiver.archivedData(withRootObject: buyLocations)
可能NSKeyedArchiver
不是线程安全的吗?
答案 0 :(得分:2)
我的问题确实是由多个线程的非同步访问引起的:
NSArchiver
遍历对象图,从根对象开始,并在遇到的每个对象的路上进行归档。
因此,为了创建正确的存档,需要在存档器终止之前不改变对象图。
在单线程应用程序中,这是自动保证的 在一个多线程应用程序中,多个线程可以改变对象图,程序员负责确保这一点,因为归档程序无法阻止其他线程改变根对象。 (例如,如果归档器首先制作对象图的深层副本,然后归档副本,则在深拷贝期间可以突变对象图)。
因此,需要序列化对象图的所有获取和设置访问,例如,通过在串行访问队列中执行它们。