为什么 `NSKeyedUnarchiver` 无法解码 `Date`

Why `NSKeyedUnarchiver` is unable to decode for `Date`

最近,我正在处理 iOS 与 NSKeyedArchiverNSKeyedUnarchiver 上的持久性问题。但是我遇到了一些问题。

代码如下:

import Foundation

class Model {
    static let instance = Model()
    static let DocumentsDirectory = FileManager().urls(for: .documentDirectory, in: .userDomainMask).first!
    static let TestArchiveURL = DocumentsDirectory.appendingPathComponent("t")

    static func getInstance() -> Model {
        return instance
    }
    var str = "String"
    var date = Date()

    func save() {
        let coder = NSKeyedArchiver(requiringSecureCoding: false)
        coder.encode(str, forKey: ModelPropertyKey.str)
        coder.encode(date, forKey: ModelPropertyKey.date)
        do {
            try coder.encodedData.write(to: Model.TestArchiveURL)
        } catch {
            print("error write to file: " + error.localizedDescription)
        }

        do {
            let data = try Data(contentsOf: Model.TestArchiveURL)
            let decoder = try NSKeyedUnarchiver(forReadingFrom: data)
            let strGot = decoder.decodeObject(forKey: ModelPropertyKey.str) as? String
            let dateGot = decoder.decodeObject(forKey: ModelPropertyKey.date) as? Date
            print("\(String(describing: strGot)) \(String(describing: dateGot))")
        } catch {
            print("Error read from file")
        }

    }
}

我用 encode(:forKey:) 编码数据并用 encodedData.write(to:) 获得持久性。然后我尝试使用 NSKeyedUnarchiver(forReadingFrom:)decodeObject(forKey:) 从磁盘读取数据。但此策略适用于 String 而适用于 Dateprint 输出:

Optional("String") nil

我想知道为什么会这样。

您需要指定要解码的对象类型,并且由于您正在解码 Date 对象,因此您需要了解 NSKeyedArchiverNSKeyedUnarchiver 将桥接您的 Date Swift 对象到 NSDate Objective-C 对象以实现 swift 和 obj-c 之间的兼容性,因为 NSKeyedArchiver 使用 NSCodingDate不符合NSCoding

要做到这一点,请将解码更改为这样

let dateGot = decoder.decodeObject(of: NSDate.self, forKey: ModelPropertyKey.date)! as Date

但是我建议您使用 swift Codable 协议与 JSONEncoderJSONDecoder 他们有一个非常好的 dateDecodingStrategy 允许指定您在编码和解码时的自定义日期格式化程序,它们还支持直接解码 Date 对象而无需桥接到 obj-c NSDate 当您尝试将对象作为 json 有效负载或解码时这很好它来自网络呼叫

这里有一个例子

let encoder = JSONEncoder()
let formatter = DateFormatter()
formatter.dateStyle = .full
formatter.timeStyle = .short
encoder.dateEncodingStrategy = .formatted(formatter)

let data = try? encoder.encode(Date())

let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(formatter)
let newDate = try? decoder.decode(Date.self, from: data!)