运行时– 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