运行时– Sam Wuu –中
为什么需要运行时:
- Objective-C是⼀门动态语言⾔,它替换⼀一些⼯作放在代码运⾏时才处理⽽并⾮编译时。也就是说,有很多类和成员变量在我们编译的时是不知道的,⽽在运⾏时,我们所编写的代码会转换成完整的确定的代码运⾏。
- 因此,编译器是不够的,我们还需要⼀个运⾏时系统(运行时系统)来处理编译后的代码。
- 苹果和GNU各⾃维护⼀个开源的运行时版本,这两个版本之间都在努⼒保持⼀致。
运行时常用接口方法
- NSObject类定义的方法
- 运行时库函数的直接调用
- 运行时相关术语的数据结构
- SEL
- ID
- IMP
运行时消息发送和消息转发
- 消息发送:
-
[people writebook:@"156"];
会被编译成:
objc_msgSend(people, @selector(writebook:), @"156");
- objc_msgSend的具体流程如下:
- 通过isa指针找到所属类
- 发现类的缓存列表 ,如果没有则下一步
- 发现类的“ 方法列表 ”(class_method_list)
- 如果能找到与选择子名称相符的方法,就跳至其实现代码
- 找不到,就逐步继承体系继续向上查找
- 如果能找到与选择子名称相符的方法,就跳至其实现代码
- 找不到,执行“ 消息转发 ”。
- 消息转发 :
- 动态方法解析:先问接收者所属的类 ,你看能不能动态添加个方法来处理这个“未知的消息”? 如果能,则消息转发结束。
-
+ (BOOL)resolveInstanceMethod:(SEL)selector // selector : 那个未知的选择子 // 返回YES则结束消息转发 // 返回NO则进入备胎
- 后备接收者:动态方法解析失败,则调用这个方法
-
- (id)forwardingTargetForSelector:(SEL)selector // selector : 那个未知的消息 // 返回一个能响应该未知选择子的备胎对象
- 消息签名:后备接收者失败,这个方法就准备要被包装成一个NSInvocation对象,在这里要先返回一个方法签名
-
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector // NSMethodSignature : 该selector对应的方法签名
- 完整的消息转发:给接收者最后一次机会把这个方法处理了,搞不定就直接程序崩溃!
- 在触发消息前,先以某种方式改变消息内容,例如追加另外一个参数,或者改变消息等等。 实现此方法时,如果发现有人调用操作不应该由本类处理,可以调用超类的同名方法。 则继承体系中的每个类别都有机会处理该请求,直到NSObject。
- 如果NSObject搞不定,则将调用doesNotRecognizeSelector:来引发异常,此时你就会在控制台看到那熟悉的选择器发送到实例。
-
- (void)forwardInvocation:(NSInvocation *)invocation // invocation : 封装了与那条尚未处理的消息相关的所有细节的对象
- 上面这4个方法均是模板方法,开发者可以重写,由运行时来调用。最常见的实现消息转发,就是转换方法3和4,忽略这个消息或者代理给其他对象。
运行时在实际开发中的应用:
- 动态添加一个类
- 通过Runtime获取一个类的所有属性:
- 一个类的所有ivar,属性和方法
- 动态变量控制
- 在NSObject的分类中增加方法来避免使用KVC赋值的时候出现崩溃
- 自动填充和解档
- 使用runtime实现字典转模型
- 利用Runtime的动态交换方法实现:
- 方法简单的交换
- 拦截系统方法(Swizzle黑魔法),也可以说成对系统的方法进行替换
- 运行时实现多继承的效果
- 动态添加方法
- 让类别可以添加属性
- 万能界面加速
- 插件开发
- JSPatch