NSKeyedArchiver unarchiveObjectWithFile аварийно завершает работу с EXC_BAD_INSTRUCTION

У меня есть следующий код, используемый для получения пути к объекту, который был заархивирован

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

Когда я запускаю этот код, он падает при вызове NSSearchPathForDirectoriesInDomains с отображением lldb

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

В представлении переменных Xcode я вижу набор строк пути, как и ожидал. Как правильно получить пользовательский каталог в Swift для архивирования/разархивирования объектов?

Обновление:

Похоже, это на самом деле происходит сбой при использовании NSKeyedUnarchiver:

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

Секундомер — это класс, который реализует NSCoding, секундомеры — это источник данных (массив секундомеров), принадлежащий представлению, выполняющему разархивирование.

Обновление 2:

Архивируемый граф объектов представляет собой массив секундомеров. NSCoding реализован следующим образом:

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()
}

Обновление 3: если для параметра expandTilde задано значение true, мой путь будет /Users/Justin/Library/Developer/CoreSimulator/Devices/FF808CCD-709F-408D-9416-E‌​E47B306309D/data/Containers/Data/Application/B39CCB84-F335-4B70-B732-5C3C26B4F6AC‌​/Documents/ArchivePath

Если я установлю expandTilde в false, у меня не будет сбоя, но файл не будет заархивирован и разархивирован, а путь будет @"~/Documents/ArchivePath"

Удаление папки приложения приводит к тому, что первый запуск приложения не приводит к сбою, но не позволяет повторно открыть его впоследствии. Кроме того, после удаления папки приложения я теперь могу прочитать путь к архиву в lldb, а не печатать его.


person JuJoDi    schedule 02.07.2014    source источник
comment
Работает без проблем в моем симуляторе iOS. Но stringByAppendingString() должен быть pathByAppendingPathComponent().   -  person Martin R    schedule 02.07.2014
comment
@MartinR обновлен новой информацией   -  person JuJoDi    schedule 02.07.2014
comment
Можете ли вы показать нам, что archivePath передается?   -  person Jack    schedule 03.07.2014
comment
@JackWu Когда я использую println, это /Users/Justin/Library/Developer/CoreSimulator/Devices/FF808CCD-709F-408D-9416-EE47B306309D/data/Containers/Data/Application/B39CCB84-F335-4B70-B732-5C3C26B4F6AC/Documents/ArchivePath, но если я пытаюсь напечатать описание с помощью lldb, я получаю Printing description of ArchivePath: (String) ArchivePath = <variable not available>   -  person JuJoDi    schedule 03.07.2014
comment
Интересно, когда вы его архивируете, он архивируется из переменной Stopwatch[]? Или это NSArray?   -  person Jack    schedule 03.07.2014
comment
Я архивирую с помощью NSKeyedArchiver.archiveRootObject(self.stopwatches, toFile: ArchivePath), а self.stopwatches определяется в классе как var stopwatches: Stopwatch []   -  person JuJoDi    schedule 03.07.2014
comment
поможет ли вам написать NSString вместо String?   -  person user102008    schedule 03.07.2014
comment
разве вы не должны вызывать super.init() в своем методе инициализации?   -  person user102008    schedule 03.07.2014
comment
@user102008 user102008 нет; да, но это не имеет значения для этого вопроса.   -  person JuJoDi    schedule 03.07.2014
comment
От какого типа произошел секундомер?   -  person Matt Gibson    schedule 03.07.2014
comment
@MattGibson Это class Stopwatch: NSObject, NSCoding {   -  person JuJoDi    schedule 03.07.2014
comment
А твой stopwatches всего лишь Stopwatch[]?   -  person Matt Gibson    schedule 05.07.2014


Ответы (1)


У меня возникла аналогичная проблема, и это было связано с искажением имени Swift. Если вы читаете из файла, созданного из закодированных объектов Objective-C, и каждый объект имеет имя класса Objective-C Stopwatch, вы не сможете напрямую декодировать его в объекты Swift, потому что Swift выполняет изменение имени класса и названия символов. То, что вы видите как Stopwatch в коде Swift, внутренне похоже на T23323234_Stopwatch_3242.

Чтобы обойти это, укажите, что ваш класс Swift должен быть экспортирован с определенным именем класса Objective-C, например:

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

где Stopwatch соответствует имени класса объектов Objective-C, которые вы заархивировали.

Редактировать: в коде проекта, на который вы ссылаетесь в комментариях, есть вторая проблема, т. е. попытка кодирования/декодирования функций, специфичных для Swift. Архивировать и разархивировать можно только объекты Objective-C. К сожалению, структуры, дженерики, кортежи и опции несовместимы с Objective-C и не могут работать с NSCoding. Лучший способ обойти это — кодировать типы Swift как экземпляры NSDictionary, например. (не проверено):

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

Изменить 2: вопреки тому, что я написал выше, это не ограничивается простым чтением экземпляров Objective-C из Swift. Похоже, что любое использование NSCoding требует имени цели-C для вашего класса. Я бы предположил, что это связано с тем, что изменение имени Swift может меняться между разными запусками компилятора.

person Bill    schedule 13.07.2014
comment
Этот проект был полностью написан на Swift, объекты Objective-C никогда не кодировались и не декодировались, что интересно. - person JuJoDi; 13.07.2014
comment
Можете ли вы опубликовать любой или весь исходный код секундомера? - person Bill; 13.07.2014
comment
Конечно - github.com/justinjdickow/stopwatch_swift_implementation/blob/ - person JuJoDi; 13.07.2014
comment
Из любопытства, вы пытались дать ему имя объекта? - person Bill; 13.07.2014
comment
Когда я использую секундомер класса @objc(Stopwatch)... (используя кавычки, которые не компилируются), я не могу декодировать объект класса (_whateverStopwatch). Также похоже, что с Beta 3 сбой теперь происходит в aCoder.encodeObject(self.laps, forKey: laps), но это то же самое EXC_BAD_INSTRUCTION - person JuJoDi; 13.07.2014
comment
Что вы имеете в виду под cannot decode object of class (_whateverStopwatch)? Это показывает искажение имени Swift? - person Bill; 14.07.2014
comment
'NSInvalidUnarchiveOperationException', причина: '*** -[NSKeyedUnarchiver decodeObjectForKey:]: невозможно декодировать объект класса (_TtC11stopwatches9Stopwatch)' - person JuJoDi; 14.07.2014
comment
Таким образом, атрибут @objc должен сделать так, чтобы вы не могли видеть там искаженные Swift имена. Попробуйте еще раз @objc(Stopwatch), затем удалите объектный файл, сохраненный в последний раз, затем снова запустите вашу программу. Я думаю, что ваша программа читает данные объекта, сгенерированные под именем Swift. Ты знаешь, что я имею в виду? - person Bill; 14.07.2014
comment
Да, поэтому я сбросил данные, из-за которых я передал ошибку невозможного декодирования объекта класса, но ошибка снова вышла в aCoder.encodeObject(self.laps, forKey: "laps") - person JuJoDi; 14.07.2014
comment
Вы не сможете кодировать/декодировать особенности языка Swift, такие как общие массивы и кортежи. Это проблема. Если вы хотите использовать NSCoding для архивирования/разархивирования этих данных, вам необходимо сначала преобразовать их в данные, полностью совместимые с Objective-C. Если вы закомментируете строки кодирования/декодирования кругов, вы не должны столкнуться с какими-либо ошибками. - person Bill; 14.07.2014
comment
Спасибо, можете ли вы добавить это к своему ответу: D У меня все равно была задача сделать круги структурой, а не кортежем, но это имеет смысл - person JuJoDi; 14.07.2014