本文我们再来介绍一下Runtime的第二个应用:反射机制
字典转类型(反射机制)
场景:
App发起网络请求后,后端返回了一大串JSON 数据。
我们需要将这个字典转化为我们定义的对象模型 Model ,比如User对象,Product对象,以便在代码中使用user.name而不是dict[@"name"]
-
不使用 Runtime :
-
在 Codable 引入前,在 OC 时代,你需要手动编写代码,假设一个模型有 20 个属性,你就要写 20 行代码;
-
如果后端增加了一个字段,或者你有 100 个不同的 Model 类,这种手动编写不仅效率低,而且容易拼写错误。
// 传统笨办法
User *user = [[User alloc] init];
user.name = dict[@"name"];
user.age = dict[@"age"];
user.address = dict[@"address"];
// ... 还有17行 ...
-
-
使用Runtime:
- 写一个通用的方法
objectWithDict:,无论什么 Model 类都能调用它,能自动把字典的值填入对象的属性中。
- 写一个通用的方法
原理:
利用 Runtime 的反射机制------指程序在运行期间可以检查自身结构的能力
核心思路:
- 利用 Runtime 的
class_copyIvarList函数,在运行时动态获取当前类中定义了哪些成员变量ivar - 遍历这些变量名
_name,_age - 处理变量名(去掉开头的下划线
_),使其变成字典里的Key(比如name,age) - 拿着 Key 去字典里找对应的值
- 利用 KVC,将找到的值赋值给对象
具体代码举例

main函数与输出结果

与Codable的区别
在纯Swift开发中,我们网络请求数据转换通常会使用Codable,是目前最标准、最推荐、也是最方便的方法。
同样的数据解析代码如下所示。

那么两者的区别有什么呢?
1.类型的容错能力
例如后端定义了一个字段是数字Int,但是返回了一个字符串 "123"。
- Codable(强类型安全 ):
- 会直接报错,解析失败,整个 Model 都会解析为nil,并抛出异常
typeMismatch - 需要开发者自定义方法解决
- 会直接报错,解析失败,整个 Model 都会解析为nil,并抛出异常
- Runtime 库(运行时动态检查与隐式转换 ):
- 解析成功
- Runtime 库通常会在内部做类型检测和转换逻辑。发现属性是
int,但字典里是NSString,会自动把"123"转成数字123赋值进去
2.处理多层嵌套的数据结构
- Codable(全自动递归 )
- 利用 Swift 强大的泛型和编译器支持
- 对于嵌套数组,编译器在编译阶段就已经知道了数组内元素的具体类型。解析时,Codable 会自动递归调用相关解析方法,无需额外配置。
- Runtime库(需手动配置 )
- Runtime 在运行时能识别出属性时 NSArray,但不知道数据里面装的是什么类型
- 必须书动编写映射代码,显示告诉Runtime,数组里面的元素类型是什么;如果忘记配置,数据会被解析为原始的NSDictionary,导致后续调用崩溃
3.性能
实际上,基于 Runtime 优化的 C 语言库往往比 Swift 的 JSONDecoder 更快。
- Codable(安全优先 ):
- Swift 的 JSONDecoder 为了安全,在底层做了大量的类型检查、中间层封装和错误抛出机制
- Runtime(效率优先 ):
- 像 YYModel 这种库,直接操作内存地址,利用 C 语言的高效性直接赋值,逃过了很多安全检查,在处理海量数据时,速度更优于原生 Codable
4.对象生命周期
例如你有一个已经存在的 User 对象,现在网络请求回来了新的数据,你只想更新这个对象的部分属性。
- Codable(只负责创建 ):
- Decodable 协议的核心是 init(from:)
- 它只能初始化并返回一个新的实例。这意味着,你会得到一个新的User实例,然后你需要手动把新实例的值一个一个赋给老实例。
- Runtime(支持热更新 ):
- 可以直接修改内存,不需要调用init
- Runtime可以遍历字典,利用KVC,把数据直接"灌入"到一个已经存在的对象实例中。