NSKeyedArchiver unarchiveObjectWithFile 崩溃与 EXC_BAD_INSTRUCTION

Posted

技术标签:

【中文标题】NSKeyedArchiver unarchiveObjectWithFile 崩溃与 EXC_BAD_INSTRUCTION【英文标题】:NSKeyedArchiver unarchiveObjectWithFile crashes with EXC_BAD_INSTRUCTION 【发布时间】:2014-08-23 16:43:01 【问题描述】:

我有如下代码,用于获取已归档对象的路径

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 []

Stopwatch 是一个实现 NSCoding 的类,stopwatches 是执行取消归档的视图所拥有的数据源(一个 Stopwatches 数组)。

更新 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()

更新 3expandTilde 设置为 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 中的存档路径,而不必打印它。

【问题讨论】:

在我的 ios 模拟器中正常工作。但是 stringByAppendingString() 应该是 pathByAppendingPathComponent()。 @MartinR 更新了新信息 您能告诉我们传入的archivePath 是什么吗? @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> 有趣的是,当您归档它时,它是从Stopwatch[] 变量归档的吗?还是NSArray 【参考方案1】:

我遇到了类似的问题,这与 Swift 的名称修改有关。如果您正在读取从编码的 Objective-C 对象生成的文件并且每个对象都有 Objective-C 类名称 Stopwatch,您将无法直接将其解码为 Swift 对象,因为 Swift 执行名称修改关于类和符号名称。你在 Swift 代码中看到的 Stopwatch 在内部类似于 T23323234_Stopwatch_3242

要绕过它,请指定应使用特定的 Objective-C 类名称导出您的 Swift 类,例如:

@objc(Stopwatch) class Stopwatch 
  ...

Stopwatch 与您已归档的 Objective-C 对象的类名匹配。

编辑:在您在 cmets 中链接的项目代码中,这里还有第二个问题,即尝试编码/解码 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:与我上面写的相反,这不仅限于从 Swift 读取 Objective-C 实例。看来NSCoding 的任何用法都需要您的类的Objective-C 名称。我想这是因为 Swift 名称修饰可以在编译器的不同运行之间发生变化。

【讨论】:

这个项目完全用 Swift 编写,从来没有 Objective-C 对象被编码或解码,虽然很有趣 你能发布任何或所有的秒表源吗? 当然 - github.com/justinjdickow/stopwatch_swift_implementation/blob/… 出于好奇,您是否尝试给它起一个 objc 名称? 当我使用@objc(Stopwatch) 类秒表...(使用引号未编译)时,我得到“无法解码类的对象(_whateverStopwatch)。它看起来也像 Beta 3失败现在在 aCoder.encodeObject(self.laps, forKey: "laps"),但它是相同的 EXC_BAD_INSTRUCTION

以上是关于NSKeyedArchiver unarchiveObjectWithFile 崩溃与 EXC_BAD_INSTRUCTION的主要内容,如果未能解决你的问题,请参考以下文章

ansibel的unarchive模块

自定义类 Unarchive 在 Cocoa Swift 中为零

ansible 之 archive&unarchive

ansible-unarchive模块

Unarchive UIImage 对象在 iOS 8 上使用 NSKeyedUnarchiver 返回 CGSizeZero 图像

NSKeyedArchiver 保存啥格式?