My app uses the following function to serialise a set of custom objects (only relevant code is shown):
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
}
The conditional compilation for the iOS and watchOS targets is required in order to allow both targets to dearchive the serialised data later, even if they have been archived by the other target.
ShoppingLocation
adopts the NSCoding
protocol, and inherits from NSObject
.
This code crashes at the let data = …
instruction with EXC_BAD_ACCESS (code=1, address=0x0)
.
I know that the error probably indicates that a non-existent object is referred. However, the variable buyLocations
is existent, and I can print the value in the debugger, including all values of the objects in the set.
The stack trace indicates that the archiver starts to archive the NSSet
, and tries to archive one of the elements of the set, but setObjectForKey
in the internal mutable dictionary fails.
What could be the reason?
EDIT:
During further testing I realised that the crash happens always when at least 2 threads of the app are active.
Particularly, I found a situation where one thread crashed (red in Xcode) at an instruction different from the one mentioned above:
let dataBlob = NSKeyedArchiver.archivedData(withRootObject: shoppingItem.buyLocations)
and the other thread stopped at the instruction mentioned above (green in Xcode):
let data = NSKeyedArchiver.archivedData(withRootObject: buyLocations)
Might it be that NSKeyedArchiver
is not thread-safe?