swift - 使用 NSKeyedArchiver 和 NSKeyedUnarchiver 进行往返编码和解码
问题描述
在实现init(coder:)
自定义 NSView 子类的过程中,我遇到了一些我仍然不完全理解的 NSKeyedArchiver 和 NSKeyedUnarchiver 的奇怪行为。考虑这个示例代码:
let label = NSTextField(labelWithString: "Test")
// Encode
let data = try NSKeyedArchiver.archivedData(withRootObject: label, requiringSecureCoding: false)
// Decode
try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? NSTextField
这似乎按预期对 NSTextField 进行编码和解码。但是,如果我尝试使用decodeTopLevelObject()
而不是unarchiveTopLevelObjectWithData(_:)
,结果是nil
:
// Encode
let data = try NSKeyedArchiver.archivedData(withRootObject: label, requiringSecureCoding: false)
// Decode
let decoder = try NSKeyedUnarchiver(forReadingFrom: data)
decoder.decodeTopLevelObject() as? NSTextField // nil
同样,如果我尝试使用encodedData
而不是archivedData(withRootObject:requiringSecureCoding:)
,结果是nil
:
// Encode
let coder = NSKeyedArchiver(requiringSecureCoding: false)
coder.encodeRootObject(label)
let data = coder.encodedData
// Decode
try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? NSTextField // nil
结果是即使nil
我使用encode(_:forKey:)
and decodeObject(forKey:)
:
// Encode
let coder = NSKeyedArchiver(requiringSecureCoding: false)
coder.encode(label, forKey: "label")
let data = coder.encodedData
// Decode
let decoder = try NSKeyedUnarchiver(forReadingFrom: data)
decoder.decodeObject(forKey: "label") as? NSTextField // nil
我很惊讶上面的第一个示例似乎可以正常工作,但其他示例都没有(尤其是最后一个)。有人可以帮我理解这里发生了什么吗?
解决方案
如果您阅读init(forReadingFrom:)的文档,它会指出:
此初始化程序默认启用 requiresSecureCoding....
这可能是您困惑的主要原因。然后,将requiresSecureCoding设置回 false 将使以下工作:
/* ENCODING */
let archiver = NSKeyedArchiver(requiringSecureCoding: false)
archiver.encodeRootObject(label) // same as .encode(label)
archiver.encode(label, forKey: "SOME_CUSTOM_KEY")
archiver.finishEncoding() // as per documentation
let data = archiver.encodedData
/* DECODING */
let unarchiver = try! NSKeyedUnarchiver(forReadingFrom: data)
// DON'T FORGET THIS!!
unarchiver.requiresSecureCoding = false
let firstResult = unarchiver.decodeTopLevelObject() as! NSTextField . // same as .decodeObject()
let secondResult = unarchiver.decodeObject(forKey: "SOME_CUSTOM_KEY") as! NSTextField
unarchiver.finishDecoding() // as per documentation
在正确编码和解码方面,只需确保您有匹配的密钥。encodeRootObject(_:)与encode(_:)实现相同,内部使用nil的键,因此只需调用decodeTopLevelObject()或decodeObject()。
另一方面,NSKeyedArchiver.archivedData(withRootObject:requiringSecureCoding:)使用密钥NSKeyedArchiveRootObjectKey,因此您可以通过执行以下技术解码:
let value = unarchiver.decodeObject(forKey: NSKeyedArchiveRootObjectKey) as! NSTextField
...但是您不想这样做,因为它是理论上可以更改的内部实现。相反,您只需使用NSKeyedArchiver.unarchiveTopLevelObjectWithData(_:),就像您在工作示例中所做的那样。
注意:如果您使用的是安全编码,还需要考虑其他因素,但我认为这超出了这个问题的范围。
推荐阅读
- ios - Kotlin - 按下按钮时运行打印语句
- c# - 信号量 C# 对象同步方法是从不同步的代码块中调用的
- javascript - 未处理的拒绝 SequelizeEagerLoadingError:电影与电影无关
- cmake - 找不到 ZEROMQTARGETS.cmake
- python - 使用列名和行索引从熊猫数据框中选择值的正确方法是什么?
- git - 我有一个行为 python 测试用例在代码预提交期间失败
- javascript - 如何创建宏以根据条件自动隐藏/显示工作表?
- java - 公共静态最终双 BASE_RATE
- java - Mockito NullPointerException - 不识别存储库
- android - Android 将元素添加到 ArrayList 里面的 onActivityResult 方法