- 添加
Codable
一致性:
- 为了让
Shape
及其子类符合Codable
协议,需要手动实现encode(to:)
和init(from:)
方法,因为自动合成无法处理多态情况。
- 首先,给
Shape
类添加一个用于标识类型的常量typeKey
,并定义一个用于编码和解码类型信息的通用方法。
class Shape: Codable {
var type: String
static let typeKey = "type"
init(type: String) {
self.type = type
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(type, forKey:.type)
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
type = try container.decode(String.self, forKey:.type)
}
private enum CodingKeys: String, CodingKey {
case type
}
}
class Circle: Shape {
var radius: Double
init(radius: Double) {
self.radius = radius
super.init(type: "circle")
}
override func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try super.encode(to: encoder)
try container.encode(radius, forKey:.radius)
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
radius = try container.decode(Double.self, forKey:.radius)
try super.init(from: decoder)
}
private enum CodingKeys: String, CodingKey {
case radius
}
}
- 接着,为
Rectangle
类实现Codable
协议:
class Rectangle: Shape {
var width: Double
var height: Double
init(width: Double, height: Double) {
self.width = width
self.height = height
super.init(type: "rectangle")
}
override func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try super.encode(to: encoder)
try container.encode(width, forKey:.width)
try container.encode(height, forKey:.height)
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
width = try container.decode(Double.self, forKey:.width)
height = try container.decode(Double.self, forKey:.height)
try super.init(from: decoder)
}
private enum CodingKeys: String, CodingKey {
case width
case height
}
}
- 自定义
JSONDecoder
:
- 为了正确解码多态类型,需要创建一个自定义的
JSONDecoder
,并实现一个init(from:)
方法来根据类型信息创建正确的实例。
extension JSONDecoder {
override func decode<T>(_ type: T.Type, from data: Data) throws -> T where T : Decodable {
guard let type = type as? [Shape].Type else {
return try super.decode(type, from: data)
}
let json = try JSONSerialization.jsonObject(with: data) as! [[String: Any]]
var shapes = [Shape]()
for item in json {
let typeString = item[Shape.typeKey] as! String
switch typeString {
case "circle":
let decoder = try JSONDecoder().decode(Circle.self, from: try JSONSerialization.data(withJSONObject: item))
shapes.append(decoder)
case "rectangle":
let decoder = try JSONDecoder().decode(Rectangle.self, from: try JSONSerialization.data(withJSONObject: item))
shapes.append(decoder)
default:
fatalError("Unsupported type: \(typeString)")
}
}
return shapes as! T
}
}
- 编码和解码操作:
let circle = Circle(radius: 5.0)
let rectangle = Rectangle(width: 10.0, height: 5.0)
let shapes: [Shape] = [circle, rectangle]
let encoder = JSONEncoder()
let data = try encoder.encode(shapes)
let decoder = JSONDecoder()
let decodedShapes = try decoder.decode([Shape].self, from: data)
性能优化
- 缓存
JSONDecoder
:
- 避免每次解码时都创建新的
JSONDecoder
实例。可以在类级别或全局级别创建一个单例JSONDecoder
实例,并重复使用。
- 使用
JSONSerialization
直接操作:
- 在自定义的
decode
方法中,已经部分使用了JSONSerialization
。可以进一步优化,直接操作JSON数据结构而不是多次编码和解码。例如,在解析出类型信息后,直接从JSON数据的字典中提取属性值,而不是先转换为Data
再解码。但这样做会使代码更复杂,需要仔细处理数据类型和错误情况。
- 减少内存分配:
- 在编码过程中,尽量减少中间数据结构的创建。例如,
JSONEncoder
在默认情况下会创建一些临时的中间数据结构。可以通过优化编码逻辑,减少不必要的内存分配。在解码时,也可以考虑提前分配足够的内存空间来存储解码后的对象,而不是动态增长数组等数据结构。