Swift UnsafePointer<T>(data.bytes).memory crash under some (but not all) 情况下

Swift UnsafePointer<T>(data.bytes).memory crash under some (but not all) circumstances

我有以下结构:

struct Identity {
    var id: Int
    var createdAt: NSDate
    var string: String
    var apnsToken: String
}

在我的应用程序执行过程中,此结构的实例 (?) 被转换为 NSData(使用以下代码)并存储在 NSUserDefaults:

var id = Identity(id: 0, createdAt: NSDate(), string: "string", apnsToken: "<apns-token-here>")
var data = NSData(bytesNoCopy: &id, length: sizeof(Identity),freeWhenDone:false)

当我尝试从 NSData 实例获取结构时,它崩溃并显示 EXC_BAD_ACCESS(它是代码 1):

var id = UnsafePointer<Identity>(userDefaultsData.bytes).memory

但是,只有当我从 NSUserDefaults 获取 NSData 实例时才会发生这种情况。如果我像下面那样做,它就不会崩溃。

var id = Identity(id: 0, createdAt: NSDate(), string: "string", apnsToken: "<apns-token-here>")
var data = NSData(bytesNoCopy: &id, length: sizeof(Identity),freeWhenDone:false)
var idPrime = UnsafePointer<Identity>(data.bytes).memory

EXC_BAD_ACCESS 转储的汇编代码在 objc_retain 中途的某处,在 and 指令之后。

更新:

我并不完全诚实。从 ObjC 中的钥匙串中检索数据,bridge_transfer 从 CF 数据对象转换为 NSData。 CF 对象作为输出参数来自 SecItemCopy()。我认为 NSUserDefaults 会更贴切。

这是因为这一行:

var data = NSData(bytesNoCopy: &id, length: sizeof(Identity),freeWhenDone:false)

不会Strings(或分配自己内存的任何其他类型,如数组)呈现为字节形式。相反,它所做的只是将指向字符串内存的 指针 序列化为 NSData 字节。

如果内存不再存在,这显然会导致爆炸。这就是为什么当您一次完成所有操作时它似乎可以正常工作,但当您存储到用户默认值然后稍后甚至可能在不同的过程中将其取回时却无法正常工作。

相反,你需要做一些事情,比如将字符串存储到它们自己的 NSData 对象中(比如 NSData(base64EncodedString:options:)),然后也存储它。