【问题标题】:Round-trip encoding and decoding with NSKeyedArchiver and NSKeyedUnarchiver使用 NSKeyedArchiver 和 NSKeyedUnarchiver 进行往返编码和解码
【发布时间】:2019-05-20 06:19:15
【问题描述】:

在为自定义 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() 而不是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

如果我使用encode(_:forKey:)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

我很惊讶上面的第一个示例似乎可以正常工作,但其他示例都没有(尤其是最后一个)。有人可以帮我理解这里发生了什么吗?

【问题讨论】:

    标签: swift nskeyedarchiver nscoder nskeyedunarchiver


    【解决方案1】:

    如果您阅读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的key,所以调用decodeTopLevelObject(),或 decodeObject()

    另一方面,NSKeyedArchiver.archivedData(withRootObject:requiringSecureCoding:) 使用密钥 NSKeyedArchiveRootObjectKey,因此您可以通过执行以下操作从技术上解码:

    let value = unarchiver.decodeObject(forKey: NSKeyedArchiveRootObjectKey) as! NSTextField
    

    ...但是您不想这样做,因为它是一个理论上可以更改的内部实现。相反,您只需使用 NSKeyedArchiver.unarchiveTopLevelObjectWithData(_:),就像您在工作示例中所做的那样。

    注意:如果您使用的是安全编码,还需要考虑其他因素,但我认为这超出了本问题的范围。

    【讨论】:

      猜你喜欢
      • 2011-08-11
      • 2012-05-10
      • 2013-08-25
      • 1970-01-01
      • 2013-12-26
      • 1970-01-01
      • 2016-05-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多