NSKeyedArchiver归档数据时崩溃

时间:2017-12-24 11:52:56

标签: ios swift thread-safety nskeyedarchiver

我的应用程序使用以下函数来序列化一组自定义对象(仅显示相关代码):

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是存在的,我可以在调试器中打印该值,包括集合中对象的所有值。

堆栈跟踪是:
enter image description here

堆栈跟踪指示归档程序开始归档NSSet,并尝试归档该组的一个元素,但内部可变字典中的setObjectForKey失败。

可能是什么原因?

编辑:

在进一步测试期间,我意识到当应用程序的至少2个线程处于活动状态时,崩溃总是发生 特别是,我发现一个线程在一个不同于上述指令的指令中崩溃(Xcode中的红色):

let dataBlob = NSKeyedArchiver.archivedData(withRootObject: shoppingItem.buyLocations)  

并且另一个线程在上面提到的指令处停止(Xcode中为绿色):

let data = NSKeyedArchiver.archivedData(withRootObject: buyLocations)

可能NSKeyedArchiver不是线程安全的吗?

1 个答案:

答案 0 :(得分:2)

我的问题确实是由多个线程的非同步访问引起的:

NSArchiver遍历对象图,从根对象开始,并在遇到的每个对象的路上进行归档。
因此,为了创建正确的存档,需要在存档器终止之前不改变对象图。

在单线程应用程序中,这是自动保证的 在一个多线程应用程序中,多个线程可以改变对象图,程序员负责确保这一点,因为归档程序无法阻止其他线程改变根对象。 (例如,如果归档器首先制作对象图的深层副本,然后归档副本,则在深拷贝期间可以突变对象图)。

因此,需要序列化对象图的所有获取和设置访问,例如,通过在串行访问队列中执行它们。