使用 NSKeyedArchiver 和 NSKeyedUnarchiver 进行往返编码和解码
Round-trip encoding and decoding with NSKeyedArchiver and NSKeyedUnarchiver
在为自定义 NSView 子类实现 init(coder:)
的过程中,我遇到了 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()
instead of 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
instead of 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
如果我使用 encode(_:forKey:)
and decodeObject(forKey:)
:
结果甚至是 nil
// 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
令我惊讶的是,上面的第一个示例似乎可以正常工作,但 none 其他示例却可以(尤其是最后一个)。有人可以帮助我了解这里发生了什么吗?
如果您阅读 init(forReadingFrom:) 的文档,它指出:
This initializer enables requiresSecureCoding by default....
这可能是您感到困惑的主要原因。将 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[的key =36=],那么只需调用 decodeTopLevelObject(),或 decodeObject().
另一方面,NSKeyedArchiver.archivedData(withRootObject:requiringSecureCoding:) 使用密钥 NSKeyedArchiveRootObjectKey,所以你可以通过执行技术解码:
let value = unarchiver.decodeObject(forKey: NSKeyedArchiveRootObjectKey) as! NSTextField
...但是您不想这样做,因为它是一个理论上可以更改的内部实现。相反,您只需使用 NSKeyedArchiver.unarchiveTopLevelObjectWithData(_:),就像您在工作示例中所做的那样。
Note: if you are using secure coding, there are other considerations to be made, but I think that's beyond the scope of this question.
在为自定义 NSView 子类实现 init(coder:)
的过程中,我遇到了 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()
instead of 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
instead of 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
如果我使用 encode(_:forKey:)
and decodeObject(forKey:)
:
nil
// 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
令我惊讶的是,上面的第一个示例似乎可以正常工作,但 none 其他示例却可以(尤其是最后一个)。有人可以帮助我了解这里发生了什么吗?
如果您阅读 init(forReadingFrom:) 的文档,它指出:
This initializer enables requiresSecureCoding by default....
这可能是您感到困惑的主要原因。将 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[的key =36=],那么只需调用 decodeTopLevelObject(),或 decodeObject().
另一方面,NSKeyedArchiver.archivedData(withRootObject:requiringSecureCoding:) 使用密钥 NSKeyedArchiveRootObjectKey,所以你可以通过执行技术解码:
let value = unarchiver.decodeObject(forKey: NSKeyedArchiveRootObjectKey) as! NSTextField
...但是您不想这样做,因为它是一个理论上可以更改的内部实现。相反,您只需使用 NSKeyedArchiver.unarchiveTopLevelObjectWithData(_:),就像您在工作示例中所做的那样。
Note: if you are using secure coding, there are other considerations to be made, but I think that's beyond the scope of this question.