Skip to content

Commit 46d9e9c

Browse files
committed
update readme
1 parent c39b556 commit 46d9e9c

7 files changed

Lines changed: 475 additions & 185 deletions

File tree

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
1-
# #suggest 1 在mapping方法中支持解析忽略
2-
3-
4-
5-
结果:目前无法支持。
1+
# #QA1 在mapping方法中支持解析忽略
62

73

84

@@ -253,4 +249,19 @@ bb0(%0 : $*Decoder, %1 : $@thin User.Type):
253249

254250
### 总结一下
255251

256-
Codable 是通过 隐式实现的 CodingKeys 实现的解析映射,如果想要改变这个必须重写CodingKeys。 目前作者没有技术方法可以控制这种重新(在Model外)。
252+
Codable 是通过 隐式实现的 CodingKeys 实现的解析映射,如果想要改变这个必须重写CodingKeys。 目前作者没有技术方法可以控制这种重新(在Model外)。
253+
254+
255+
256+
## 使用 @IgnoredKey 忽略
257+
258+
```
259+
struct Home: SmartCodable {
260+
var name: String = ""
261+
262+
@IgnoredKey
263+
var area: String = ""
264+
}
265+
```
266+
267+
作者使用属性包装器 `@IgnoredKey` 忽略掉json中的值解析,进而实现了 **伪忽略**,实质上还是会解析,只是不使用json值,直接进入失败兜底逻辑,使用属性的初始化值替代。

Document/QA/QA2.md

Lines changed: 304 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,304 @@
1+
# #QA2 支持继承关系的解析
2+
3+
4+
5+
## HandyJSON 如何处理继承的解析
6+
7+
`HandyJSON` 能自动处理继承层级中的所有属性,子类无需额外实现任何方法,使用方式非常简洁,示例如下:
8+
9+
```
10+
class BaseModel: HandyJSON {
11+
var name: String = ""
12+
required init() { }
13+
}
14+
15+
class Model: BaseModel {
16+
var age: Int = 0
17+
}
18+
19+
let dict = [
20+
"name": "小明",
21+
"age": 10
22+
] as [String : Any]
23+
24+
guard let model = Model.deserialize(from: dict) else { return }
25+
print(model.age) // 10
26+
print(model.name) // 小明
27+
```
28+
29+
30+
31+
32+
33+
## SmartCodable 的继承
34+
35+
```
36+
class BaseModel: SmartCodable {
37+
var name: String = ""
38+
required init() { }
39+
}
40+
41+
@SmartSubclass
42+
class Model: BaseModel {
43+
var age: Int = 0
44+
}
45+
46+
let dict = [
47+
"name": "小明",
48+
"age": 10
49+
] as [String : Any]
50+
51+
guard let model = Model.deserialize(from: dict) else { return }
52+
print(model.age) // 10
53+
print(model.name) // 小明
54+
```
55+
56+
> ⚠️ 需要使用5.0+版本。如果使用低版本,需要使用下面提供的方案。
57+
58+
59+
60+
61+
62+
63+
64+
## Codable 在4.0+如何处理继承的解析
65+
66+
Swift 编译器仅会对显式遵循`Codable`协议的**当前类型**自动合成编解码方法。
67+
68+
当父类遵循 `Codable` 时,其自身的属性会被自动处理。但子类新增属性不会被自动处理,因此我们需要**重写编解码方法,手动实现新增属性的编解码逻辑,并调用super实现**
69+
70+
例如:(相比 `HandyJSON`显得繁琐一些😓)
71+
72+
```
73+
class BaseModel: Codable {
74+
var name: String = ""
75+
required init() { }
76+
}
77+
78+
class SubModel: BaseModel {
79+
var age: Int = 0
80+
81+
private enum CodingKeys: CodingKey {
82+
case age
83+
}
84+
85+
required init(from decoder: Decoder) throws {
86+
let container = try decoder.container(keyedBy: CodingKeys.self)
87+
self.age = try container.decode(Int.self, forKey: .age)
88+
try super.init(from: decoder)
89+
}
90+
91+
override func encode(to encoder: Encoder) throws {
92+
try super.encode(to: encoder)
93+
var container = encoder.container(keyedBy: CodingKeys.self)
94+
try container.encode(age, forKey: .age)
95+
}
96+
97+
required init() { super.init() }
98+
}
99+
```
100+
101+
102+
### 为什么子类必须手动实现?
103+
104+
我们可以通过`SIL(Swift Intermediate Language)`验证编译器的行为。
105+
```
106+
class BaseModel : Decodable & Encodable {
107+
108+
@_hasStorage @_hasInitialValue var name: String { get set }
109+
required init()
110+
111+
enum CodingKeys : CodingKey {
112+
case name
113+
114+
func hash(into hasher: inout Hasher)
115+
init?(stringValue: String)
116+
init?(intValue: Int)
117+
var hashValue: Int { get }
118+
var intValue: Int? { get }
119+
var stringValue: String { get }
120+
}
121+
122+
@objc deinit
123+
func encode(to encoder: Encoder) throws
124+
required init(from decoder: Decoder) throws
125+
}
126+
```
127+
128+
```
129+
@_inheritsConvenienceInitializers class SubModel : BaseModel {
130+
@_hasStorage @_hasInitialValue var age: Int { get set }
131+
required init()
132+
required init(from decoder: Decoder) throws
133+
@objc deinit
134+
}
135+
```
136+
137+
可以看到:
138+
139+
- 对于父类,由于显式遵循了 `Codable` 协议,编译器自动合成了`init(from decoder:)``encode(to encoder:)``CodingKeys`
140+
141+
- 对于子类:
142+
143+
- **不会自动合成** `encode(to:)`(不是 `required` 方法, 子类也没有显式的遵循`Codable`协议)
144+
145+
146+
- **不会自动合成** `CodingKeys`(没有显式的遵循`Codable`协议)
147+
148+
149+
- 会合成 `init(from:)`(因为是`required`初始化方法,合成的这个方法中**不会包含子类新增属性的解码逻辑**
150+
151+
152+
因此,若子类也有需要被编码/解码的属性, 就**必须在子类中重写** `init(from:)``encode(to:)`
153+
154+
155+
156+
## SmartCodable 如何处理继承的解析
157+
158+
`SmartCodable` 是对原生 `Codable` 的增强,天然支持`Codable`继承的处理方案,也提供了其它方案选择,可以根据各自项目的情况选择最优方案。
159+
160+
- 基于继承的实现(类似原生Codable)
161+
- 基于Protocol的实现
162+
- 基于@SmartFlat的实现
163+
- 基于Protocol + @SmartFlat的混合实现
164+
165+
166+
### 方案一:基于继承的实现(同Codable)
167+
与原生 Codable 实现类似,但使用 `SmartCodable` 增强解析器,具备类型容错能力。
168+
169+
**优点:**
170+
171+
- 原生 `Codable` 写法,符合直觉
172+
- 支持类型不一致、字段缺失、nil等场景的容错
173+
174+
**缺点:**
175+
176+
- 子类仍需手动实现新增属性的编解码逻辑,代码量较多
177+
178+
```
179+
class BaseModel: SmartCodable {
180+
var name: String = ""
181+
required init() { }
182+
}
183+
184+
class SubModel: BaseModel {
185+
var age: Int = 0
186+
187+
private enum CodingKeys: CodingKey {
188+
case age
189+
}
190+
191+
required init(from decoder: Decoder) throws {
192+
let container = try decoder.container(keyedBy: CodingKeys.self)
193+
self.age = try container.decode(Int.self, forKey: .age)
194+
try super.init(from: decoder)
195+
}
196+
197+
override func encode(to encoder: Encoder) throws {
198+
try super.encode(to: encoder)
199+
var container = encoder.container(keyedBy: CodingKeys.self)
200+
try container.encode(age, forKey: .age)
201+
}
202+
203+
required init() { super.init() }
204+
}
205+
```
206+
207+
### 方案二:基于Protocol的实现
208+
通过协议定义公共属性,避免继承带来的复杂度。适用轻量级共享属性定义。
209+
**优点:**
210+
211+
- 不需要手动实现子类编解码逻辑
212+
- 子类间具有协议这个公共类型
213+
214+
**缺点:**
215+
216+
- 每个子类都需实现协议中的属性,存在一定重复
217+
```
218+
protocol BaseModel {
219+
var name: String { set get }
220+
var sex: Int { set get }
221+
}
222+
223+
class SubModel: BaseModel, SmartCodable {
224+
required init() {}
225+
226+
var name: String = ""
227+
var sex: Int = 0
228+
229+
var age: Int = 0
230+
}
231+
```
232+
233+
### 方案三:基于@SmartFlat的实现
234+
使用组合代替继承,将父类作为属性嵌入到子类中。`@SmartFlat`属性包装器会从当前JSON节点提取数据填充该属性。
235+
**优点:**
236+
237+
- 不需要手动实现子类编解码逻辑
238+
- 避免了`Protocol`方案中,各子类重复实现基协议的繁琐
239+
240+
**缺点:**
241+
242+
- 缺失子类间的公共类型
243+
```
244+
class BaseModel: SmartCodable {
245+
required init() {}
246+
247+
var name: String = ""
248+
var sex: Int = 0
249+
}
250+
251+
class SubModel: SmartCodable {
252+
required init() {}
253+
254+
var age: Int = 0
255+
256+
@SmartFlat
257+
var manBase: BaseModel = .init()
258+
}
259+
260+
let dict = [
261+
"name": "小明",
262+
"sex": 1,
263+
"age": 10,
264+
] as [String : Any]
265+
266+
guard let model = SubModel.deserialize(from: dict) else { return }
267+
print(model.manBase.name) // 小明
268+
print(model.manBase.sex) // 1
269+
print(model.age) // 10
270+
```
271+
272+
### 方案四:基于Protocol + @SmartFlat的实现
273+
结合`Protocol``@SmartFlat`两种方案的优点,规避各自的不足,比较灵活。
274+
**优点:**
275+
276+
- 不需要手动实现子类编解码逻辑
277+
- 避免了`Protocol`方案中,各子类重复实现基协议的繁琐
278+
- 避免了`@SmartFlat`方案中,缺失了各子类的公共类型约束
279+
280+
**缺点:**
281+
282+
- 不是真正的继承😂
283+
284+
```
285+
protocol ManBaseModelProtocol {
286+
var manBase: BaseModel { set get }
287+
}
288+
289+
class BaseModel: SmartCodable {
290+
required init() {}
291+
292+
var name: String = ""
293+
var sex: Int = 0
294+
}
295+
296+
class SubModel: SmartCodable, ManBaseModelProtocol {
297+
required init() {}
298+
299+
@SmartFlat
300+
var manBase: BaseModel = .init()
301+
302+
var age: Int = 0
303+
}
304+
```

0 commit comments

Comments
 (0)