简介
Objective-C(下面简称OC) 是基于面向对象思想而对C 语言进行扩展的一种动态编程语言,而这个扩展的核心是Runtime。Runtime是一个用C语言和汇编语言写的动态库,它是整个OC运行框架的一块基石。
与Runtime交互
与Runtime交互有三种方式,分别是:
1.通过Objective-C 源代码
大部分情况下我们都是使用OC在XCode开发工具上进行项目开发,而不需要涉及到Runtime的使用。比如,当我们调用对象方法-(void)doSomething:(NSString *)str时,只需要在键盘上敲出"[self doSomething:@"xxx"];"即可,编译过程中系统会自动帮我们转换为"objc_msgsend(person,@selector(doSomething),@"xxx");"。
2.通过 Foundation 框架的NSObject类定义的方法
Cocoa中大部分类都是NSObject的子类,都继承了NSObject的行为。NSObject类中一些方法可以从Runtime系统中获取信息,如:
-class 方法返回对象的类;
-isKindOfClass:和-isMemberOfClass:方法检查对象是否存在于指定的类的继承体系中(是否是其子类或者父类或者当前类的成员变量);
-respondsToSelector:检查对象能否响应指定的消息;
3.通过对 runtime 函数的直接调用
Runtime 系统是一个由一系列函数和数据结构组成,具有公共接口的动态共享库。使用前需要先导入<objc/message.h>、<objc/runtime.h>。类的操作方法大部分是以class为前缀的,而对象的操作方法大部分是以objc或object为前缀。
Runtime基础数据结构
1.Class
Class实际上是一个指向objc_class结构体的指针,对objc_class结构体的定义如下
定义中有几个重要的字段:
1)isa:Objc中所有的类自身也是一个对象,这个对象的class也有一个isa指针,指向metaClass元类。
2) super_class: 指向该类的父类,如果该类是顶部根类,则super_class 为NULL。
3) cache: 用于缓存最近使用的方法,该方法可以有效提高查询指定方法的效率。
2.Meta Class(元类)
meta class 是一个类对象的类,当我们向一个对象发送消息时,runtime会在这个对象所属的类的方法列表中查找方法;而向一个类发送消息时,会在这个类的meta class的方法列表中查找。而meta class作为一个类,它的isa指向基类的meta class,基类的meta class的isa指针又指向它自己。
3.Method
Method是一种代表类中的某个方法的类型。图中的函数为为类添加方法,从参数可知Method结构体有SEL字段、IMP字段、char字段。SEL类型仅代表方法名,即使相同方法名的方法在不同类中定义,但方法选择器还是相同的。types表示方法的参数类型和返回值类型。IMP是一个函数指针,指向了函数体,即函数的实现。
消息发送
当消息发送给一个对象时,objc_msgSend通过对象的isa指针获取到类的结构体,然后在方法列表中查找方法的selector。如果没有找到selector,则通过super_class找到父类,在父类的方法列表中查找selector,以此类推。若最后没有找到selector,则会进行消息转发。
消息转发
当以"[object message]"方式调用方法时候,若找不到selector则编译器会报错。若以“perform...”形式来调用,则需要等到运行时才能确定object是否能接收message消息。如果不能则程序崩溃。消息转发机制分为三个步骤:
1).动态方法解析
2).备用接收者
3). 完整转发
1.动态方法解析
对象接收到未知消息时,调用resolveInstanceMethod:。在这个方法中,我们有机会为该未知消息新增一个处理方法。
2.备用接收者
若没有新增处理方法,则会调用forwardingTargetForSelector方法。如果一个对象实现了这个方法,并返回一个非nil的结果,则这个对象会作为消息的新接收者,而且消息会被分发到这个对象。
3.完整消息转发
若还是不能处理未知消息,则只能启用完整的消息转发机制。调用forwardInvocation:方法。对象会创建一个表示消息的NSInvocation 对象。把与消息有关的信息都封装到NSInvocation里面,包括selector、target和参数。在该方法中选择将消息转发给其它对象。还有一点,我们需要重写methodSignatureForSelector:方法,消息转发机制从这个方法获取的信息来创建NSInvocation对象,因为我们需要重写这个方法并为给定的selector提供一个合适的方法签名。PS:下图Viewcontroller只是一个普通的类。
整个过程如下图所示:
Method Swizzling(方法替换)
在Objc中,运行时会自动调用每个类的两个方法。+load 会在类初始加载时调用,+initialize会在第一次调用类的类方法或实例方法之前被调用。由于方法替换会影响到类的全局状态,因此要尽量避免在并发处理中出现竞争情况。
要注意的是swizzling应该总在+load且dispatch_once中执行。+load能保证在类的初始化过程中被加载,并保证这种改变应用级别的行为的一致性。相比之下,+initialize在其执行时不提供这种保证。
总结
深入理解Runtime 更有利于我们利用消息机制写出更强大的代码,菜鸟一枚,请喷轻一点~
参考博客:http://yulingtianxia.com/blog/2014/11/05/objective-c-runtime/
http://southpeak.github.io/categories/objectivec/