I have the following struct:
struct Identity {
var id: Int
var createdAt: NSDate
var string: String
var apnsToken: String
}
Over the course of my application's execution, instances (?) of this struct are turned into NSData
(using the following code) and stored in NSUserDefaults
:
var id = Identity(id: 0, createdAt: NSDate(), string: "string", apnsToken: "<apns-token-here>")
var data = NSData(bytesNoCopy: &id, length: sizeof(Identity),freeWhenDone:false)
When I try to get a struct from the NSData
instance, it crashes with an EXC_BAD_ACCESS (it's a code 1):
var id = UnsafePointer<Identity>(userDefaultsData.bytes).memory
However, this only happens when I get the NSData
instance from NSUserDefaults. If I do something like below, it works without a crash.
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
The assembly code dumped by the EXC_BAD_ACCESS is somewhere halfway through objc_retain, after an and
instruction.
UPDATE:
I wasn't completely honest. The data is retrieved from the keychain in ObjC, bridge_transfer cast to an NSData
from a CF data object. The CF object comes from SecItemCopy()
as an out param. I thought NSUserDefaults
would be more relatable.
This is because this line:
var data = NSData(bytesNoCopy: &id, length: sizeof(Identity),freeWhenDone:false)
will not render the String
s (or any other type that allocates its own memory, like an array) into byte form. Instead all it will do is serialize the pointer to the string’s memory into the NSData
bytes.
This obviously is going to lead to explosions if the memory is no longer there. Which is why it might seem to be working when you do it all in one go, but not when you store to user defaults and then, later or maybe even in a different process, get it back out.
Instead, you’ll need to do something like store the strings into their own NSData
objects (say with NSData(base64EncodedString:options:)
) and then store that too.