如何使用NSKeyedUnarchiver.unarchiveObject进行解档

21
我有一段工作的代码,但它已经过时了:
这部分是好的:
 let archived = try? NSKeyedArchiver.archivedData(withRootObject: [defaultRecord] as NSArray, requiringSecureCoding: false)

这个已被弃用:

 let records = NSKeyedUnarchiver.unarchiveObject(with: unarchivedObject as Data) as? [Record]
'unarchiveObject(with:)' 在iOS 12.0中已被弃用:请使用+unarchivedObjectOfClass:fromData:error:代替
听起来很简单,但我找不到一种方法来使用建议的方法,而不会在尝试每种组合时都出现错误。
有这方面的有效示例吗?

2
尝试使用unarchiveTopLevelObjectWithData。最好查看此线程 - OOPer
5个回答

21

使用新API对数组进行归档的方法有些棘手。

如果您不忽略try?的错误,您就可以自己找出答案。

要能够解码一个自定义类的数组,请使用复数形式unarchivedObject(ofClasses:from:并指定NSArray(!)和自定义类。此外,您的类必须采用NSSecureCoding协议。

class Record : NSObject, NSSecureCoding {

   static var supportsSecureCoding: Bool {
        return true
    }

....

do {
    let archived = try NSKeyedArchiver.archivedData(withRootObject: [defaultRecord], requiringSecureCoding: false)

    let records = try NSKeyedUnarchiver.unarchivedObject(ofClasses: [NSArray.self, Record.self], from: archived) as! [Record]
    print(records)
} catch { print(error) }

但是为什么你要将 defaultRecord 存档为数组呢?如果你将单个对象存档,你可以保留你的类,然后写下:

do {
    let archived = try NSKeyedArchiver.archivedData(withRootObject: defaultRecord, requiringSecureCoding: false)

    let record = try NSKeyedUnarchiver.unarchivedObject(ofClass: Record.self, from: archived)
    let records = [record]
    print(records)
} catch { print(error) }

顺便说一下:考虑使用Codable对类进行序列化。这样更符合Swift的风格,而且不需要继承自NSObject


1
如果你的类已经采用了 Codable,就可以放弃使用 SwiftyJSONNSCoding(甚至是 NSObject)了。 - vadian
删除SwiftyJson CocoaPod,我的代码会继续工作吗?还是需要进行大量重构? - Spring
1
你需要进行一些重构,但这非常值得。 Codable 是内置的(没有依赖项),比 SwiftyJSON 更高效。最终它将比现在少写一些代码。 - vadian
是的,这两个协议看起来相似,但它们彼此之间没有关联。 - vadian
非常微妙的差别,感谢您指出复数形式@vadian! - Sebastian Westemeyer
显示剩余2条评论

4

以上方法在我解压缩时无效,我尝试了以下方法并成功:

NSKeyedUnarchiver.unarchivedObject(ofClasses: [Record.self], from: archived)

1
如果有人对Objective C代码感兴趣,这是它的代码:
// pointer to storage for error message
NSError *error = nil;

// read data from "dataPath" location
NSData *codedData = [[NSData alloc] initWithContentsOfFile:dataPath];

// create a set of root class (the array) and the data object class
NSSet<Class> *classes = [NSSet setWithObjects:NSArray.class, Record.class, nil];

// read using classes, not only single class
NSArray *data = [NSKeyedUnarchiver unarchivedObjectOfClasses:classes fromData:codedData error:&error];

再次强调,这只是对@vadian正确指出的代码的“翻译”(并且救了我一天!)。


1

我使用了这个解决方案,保持逻辑不变并隐藏警告:

func clone<T: UIView>() throws -> T {
    let data = try NSKeyedArchiver.archivedData(withRootObject: self, requiringSecureCoding: false)
    let unarchiver = try NSKeyedUnarchiver(forReadingFrom: data)
    unarchiver.requiresSecureCoding = false
    return try unarchiver.decodeTopLevelObject(of: T.self, forKey: "root")
}

谢谢。看起来工作得很好。你是怎么确定密钥应该是“root”的?我在哪里可以阅读更多相关信息? - undefined
我记不太清楚了,但我猜如果你将数据保存到文件中,并用文本编辑器打开,'root'这个词会在那里可见。我只是在这里使用了最快的方法。另一种可能性是使用系统方法,但我对此无法提供太多帮助。 - undefined

-3

Xcode 14 + iOS16

以下是一个归档和解档记录的代码示例:

guard
    let archivedRecord = try? NSKeyedArchiver.archivedData(withRootObject: defaultRecord, requiringSecureCoding: false),
    let record = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(archivedRecord) as? Record
else { return nil }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接