一般来说,Objective-C 通过 NSCoding 归档数据,Swift 采用 Codable 方式。但是并不意味着它们无法协同工作,需要一些很少的工作就可以将 NSCoding 的数据在 Codable 内部进行归档。怎么理解?
比方说, UIColor 和 UIImage 实现了 NSCoding 但是并没有实现 Codable。
这里我们通过一个简单的 struct 举例:
struct Person {
var name: String
var favoriteColor: UIColor
}
复制代码如何将 Person 实现 Codable, 然后通过 JSONEncoder 进行归档?
我们将按这 4 个步骤:
- 创建
Persion的extension,且遵守Codable协议 - 创建自定义的
CodingKey,用以描述哪些数据会被保存 - 实现
init(from:)方法,将原始数据转化为UIColor类型 - 实现
encode(to:)方法,将UIColor数据转为原始数据,这样Codable可以将其 base-64 编码
首先给 Persion 添加 extension:
extension Persion: Codable {
}
复制代码添加后,编译是失败的,因为 UIColor 没有实现 Codable。那我们继续第二步:添加自定义的 coding keys。
enum CodingKeys: String, CodingKey {
case name
case favoriteColor
}
复制代码我们需要通过声明这些 coding keys 进行手动地编码和解码。
实现解码操作:
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
name = try container.decode(String.self, forKey: .name)
// 获取到原始数据
let colorData = try container.decode(Data.self, forKey: .favoriteColor)
// 进行解码
favoriteColor = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(colorData) as? UIColor ?? UIColor.black
}
复制代码实现编码操作:
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(name, forKey: .name)
// 转化为 原始数据
let colorData = try NSKeyedArchiver.archivedData(withRootObject: favoriteColor, requiringSecureCoding: false)
// 进行编码
try container.encode(colorData, forKey: .favoriteColor)
}
复制代码上面的工作完成后,我们就可以愉快的玩耍了。
let taylor = Person(name: "Swift", favoriteColor: .blue)
let encoder = JSONEncoder()
do {
let encoded = try encoder.encode(taylor)
let str = String(decoding: encoded, as: UTF8.self)
print(str)
} catch {
print(error.localizedDescription)
}
复制代码