Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSKeyedArchiver unarchiveObjectWithFile crashes with EXC_BAD_INSTRUCTION

I have the following code, used to get the path of an object that has been archived

let paths = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)
let path = paths[0] as String
let archivePath = path.stringByAppendingString("archivePath")

When I run this code, it crashes at the NSSearchPathForDirectoriesInDomains call with lldb showing

Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)

In Xcode's Variables View I see the path String set as I would expect. What is the proper way to get a user directory in Swift for archiving/unarchiving objects?

Update:

It appears this is actually crashing on my use of the NSKeyedUnarchiver:

stopwatches = NSKeyedUnarchiver.unarchiveObjectWithFile(archivePath) as Stopwatch []

Stopwatch is a class that implements NSCoding, stopwatches is the datasource (an array of Stopwatches) owned by the view doing the unarchiving.

Update 2:

The object graph being archived is an array of Stopwatches. NSCoding is implemented as follows:

func encodeWithCoder(aCoder: NSCoder!) {
    aCoder.encodeBool(self.started, forKey: "started")
    aCoder.encodeBool(self.paused, forKey: "paused")
    aCoder.encodeObject(self.startTime, forKey: "startTime")
    aCoder.encodeObject(self.pauseTime, forKey: "pauseTime")
    aCoder.encodeInteger(self.id, forKey: "id")
}

init(coder aDecoder: NSCoder!) {
    self.started = aDecoder.decodeBoolForKey("started")
    self.paused = aDecoder.decodeBoolForKey("paused")
    self.startTime = aDecoder.decodeObjectForKey("startTime") as NSDate
    self.pauseTime = aDecoder.decodeObjectForKey("pauseTime") as NSDate
    self.id = aDecoder.decodeIntegerForKey("id")
    super.init()
}

Update 3: With expandTilde set to true my path is /Users/Justin/Library/Developer/CoreSimulator/Devices/FF808CCD-709F-408D-9416-E‌​E47B306309D/data/Containers/Data/Application/B39CCB84-F335-4B70-B732-5C3C26B4F6AC‌​/Documents/ArchivePath

If I set expandTilde to false, I don't get the crash, but the file is not archived and unarchived, and the path is @"~/Documents/ArchivePath"

Deleting the Application folder causes the first launch of the application to not crash, but does not allow it to reopen afterwards. Also, after deleting the application folder, I am now able to read the archive path in lldb rather than having to println it.

like image 822
JuJoDi Avatar asked Jul 02 '14 18:07

JuJoDi


1 Answers

I experienced a similar problem and it had to do with Swift's name mangling. If you're reading from a file that was generated from encoded Objective-C objects and each object had the Objective-C class name Stopwatch, you won't be able to directly decode that into Swift objects, because Swift performs name mangling on class and symbol names. What you see as Stopwatch in Swift code is internally something like T23323234_Stopwatch_3242.

To get around it, specify that your Swift class should be exported with a specific Objective-C class name, e.g.:

@objc(Stopwatch) class Stopwatch {
  ...
}

where Stopwatch matches the class name of the Objective-C objects you've archived.

Edit: In the project code you linked in the comments, there's a second problem here, i.e. trying to encode/decode Swift-specific features. Only Objective-C objects can be archived and unarchived. Sadly, structs, generics, tuples and optionals are not Objective-C compatible and can't work with NSCoding. The best way to work around this is to encode Swift types as NSDictionary instances, e.g. (untested):

let encodedLaps = self.laps.map { lap => ["start": lap.start, "end": lap.end] }
aCoder.encodeObject(encodedLaps, forKey: "laps")

Edit 2: Contrary to what I wrote above, this isn't limited to just reading Objective-C instances from Swift. It appears that any usage of NSCoding requires an Objective-C name for your class. I would imagine this is because Swift name-mangling can change between different runs of the compiler.

like image 76
Bill Avatar answered Oct 27 '22 02:10

Bill