一、概述
Objective-C 是一个动态语言,这意味着它需要一个编译器,也需要一个运行时系统来动态得创建类和对象、进行消息传递和转发。OC想要进行执行要转为C语音和汇编语音。而这个转换的过程就是Runtime实现的。Runtime是使用C和汇编写的Runtime库,是OC面向对象和动态机制的基石。Runtime的主要特性是消息传递,如果消息在对象方法中找不到就进行转发。
二、消息传递
一个对象的方法像这样[obj foo],编译器转成消息发送objc_msgSend(obj, foo),Runtime时执行的流程是这样的:
首先,通过obj的isa指针找到它的 class ,然后在 class 的 method list 找 foo方法 ;如果它的 class 中没有foo方法,则继续前往 superClass 中寻找 ;找到 foo 这个函数,就去执行它的实现IMP 。这样的查找效率有点低,objc_class提供一个objc_cache方法,找到foo 方法之后,把foo方法 的method_name 作为key ,method_imp作为value 给存起来。等再次执行foo方法的时候不用去method list中查找,直接在cache中查找。
1.消息传递的实现:
系统首先找到消息的接收对象,然后通过对象的isa找到它的类。在它的类中查找method_list,是否有selector方法。没有则查找父类的method_list。找到对应的method,执行它的IMP。转发IMP的return值。
1.1类对象(objc_class):由Class类型来表示的,是一个指向objc_class结构体的指针
objc_class结构体:struct objc_class {
Class isa; // 实现方法调用的关键
Class super_class; // 父类
const char * name; // 类名
long version; // 类的版本信息,默认为0
long info; // 类信息,供运行期使用的一些位标识
long instance_size; // 该类的实例变量大小
struct objc_ivar_list * ivars; // 该类的成员变量链表
struct objc_method_list ** methodLists; // 方法定义的链表
struct objc_cache * cache; // 方法缓存
struct objc_protocol_list * protocols; // 协议链表
};
1.2实例(objc_object):实例中保存着isa指针
1.3元类(Meta Class):类对象和类方法创建是isa指针指向的结构体创建,类对象的isa指针指向的我们称之为元类(metaclass),元类中保存了创建类对象以及类方法所需的所有信息。
1.4Method(objc_method):表示能够独立完成一个功能的一段代码
typedef struct objc_method *Method;
struct objc_method {
SEL method_name;
char * method_types;
IMP method_imp;
};
1.5SEL(objc_selector):保存方法名的字符串
1.6 IMP:是函数指针,就是用来找到函数地址,然后执行函数;在Objective-C中,所有的方法调用,都会转化成向对象发送消息。发送消息主要是使用objc_msgSend函数。函数结构:id objc_msgSend(id self, SEL op, ...)。因此,OC的方法调用就是发送消息。例如:
[person foo];
objc_msgSend(person,foo];
1.7类缓存(objc_cache):通过OC运行时的isa指针检查对象,它能找到一个有很多方法的对象。但是,你只使用一部分,以后每次查找时,都要搜索所有选择器的类分派表,这样效率比较慢。因此类实现一个缓存,每当你搜索一个类分派表,并找到相应的选择器,它把它放入它的缓存。所以当objc_msgSend查找一个类的选择器,它首先搜索类缓存。
1.8Category(objc_category):一个指向分类的结构体的指针,在objc_category中包含对象方法列表、类方法列表、协议列表。因此Category支持添加对象方法、类方法、协议,但不能保存成员变量。category可以添加属性,但是不会生成setter和getter方法,我们可以通过「关联对象」的方式来添加可用的属性。在.h中声明property,在.m 中导入#import<<objc/runtime.h>,实现objc_setAssociatedObject和objc_getAssociatedObject方法来达到目的。
总结:当向一个对象发送消息时,会去这个类的 methodLists 中查找相应的 SEL ,如果查不到,则通过 super_class 指针找到父类,再去父类的 methodLists 中查找,一直向上找。最后仍然找不到,抛出异常。
三、消息转发
当找不到对应的方法,会抛出异常。对象在找不到方法,到抛出异常这个过程中发的事情就是拦截调用和消息转发。在程序崩溃之前重写+resolveClassMethod:和+resolveInstanceMethod:方法,可以做一些其他处理。可以动态添加一个方法,并返回YES告诉程序已经成功处理消息。如果这两个方法都返回NO,按照下图方法进行执行。最后三次机会:
动态方法解析
备用接收者
完整消息转发
![](https://img.haomeiwen.com/i16428911/19874e11dbdc7a0e.png)
1.重写+resolveClassMethod:和+resolveInstanceMethod:方法返回YES,可以使用class_addMethod动态添加方法,然后执行该方法的IMP。
2.当+resolveInstanceMethod:方法返回NO,就会执行forwardingTargeForSelector:方法。如果对象实现了forwardingTargeForSelector:,就会把方法交给其他对象来进行实现。
3.如果forwardingTargeForSelector不能处理,就会交给methodSignatureForSelector执行,获得函数的参数与返回值类型。如果返回nil,Runtime则会发出 -doesNotRecognizeSelector: 消息,程序崩溃。如果返回一个methodSignature,则执行forwardInvocation:方法。
四、Runtime应用
1.Method Swizzling 方法交换、添加
通过class_addMethod(被添加方法的类,添加的方法的名称的SEL, 方法的实现, 类型编码)添加方法。
交换方法主要想修改系统的方法实现
+(void)load {
// 获得两个需要交换的方法
Method originalMethod=class_getInstanceMethod(class,@selector(originalSelector));
Method swizzledMethod=class_getInstanceMethod(class,@selector(swizzledSelector));
// 尝试添加需要交换的方法,若添加失败,则说明已经有该方法,直接接交换if(!class_addMethod((class),@selector(originalSelector),method_getImplementation(swizzledMethod),method_getTypeEncoding(swizzledMethod))){
method_exchangeImplementations(originalMethod,swizzledMethod);
}else{
// 如果添加成功,说明没有该方法,则把刚添加的方法替换为我们需要的方法class_replaceMethod((class),@selector(swizzledSelector),method_getImplementation(originalMethod),method_getTypeEncoding(originalMethod));}}
2.关联对象--给分类添加属性
category可以添加属性,但是不会生成setter和getter方法,我们可以通过「关联对象」的方式来添加可用的属性。在.h中声明property,在.m 中导入#import<<objc/runtime.h>,实现objc_setAssociatedObject和objc_getAssociatedObject方法来达到目的。
//关联对象
voidobjc_setAssociatedObject(id object, constvoid*key, id value, objc_AssociationPolicy policy)
//获取关联的对象
id objc_getAssociatedObject(id object, constvoid*key)
//移除关联的对象
voidobjc_removeAssociatedObjects(id object)
//我是参数解释
id object:被关联的对象
const void *key:关联的key,要求唯一
id value:关联的对象
objc_AssociationPolicy policy:内存管理的策略
3.热修复
JSPatch是一个 iOS 动态更新框架,使用JS 可以调用任何 Objective-C 原生接口,为项目动态添加模块,或替换项目原生代码动态修复 bug。能够实现消息转发,还可以实现方法添加、替换能一系列功能。
4.获取列表
unsigned int count;
//获取属性列表
objc_property_t *propertyList = class_copyPropertyList([selfclass], &count);
//获取方法列表
Method *methodList = class_copyMethodList([selfclass], &count);
//获取成员变量列表
Ivar *ivarList = class_copyIvarList([selfclass], &count);
//获取协议列表
__unsafe_unretained Protocol **protocolList = class_copyProtocolList([selfclass], &count);
网友评论