首先要知道我们写的代码在程序运行过程中都会被转化成runtime的C代码执行。
runtime突出的一点就是OC中消息传递机制的应用。objc_msgsend(target,SEL);
首先我们先看一下runtime的定义文件
OC中的一切都被设计为了对象,这些对象在Runtime中用struct(结构体)来表示。
下面来看一下runtime的常用功能获取类的系列列表
定义如下一个类:
1、获取列表
- 获取属性列表
- 获取方法列表
- 获取成员变量列表
- 获取遵循的协议列表
2、方法调用
方法调用在runtime(运行时)的调用过程,例如【self way】在runtime中会被转化为 objc_msgSend(self, @selector( way )).
如果用实例对象调用实例方法,那么系统回到实例对象的 isa 指针指向的对象中找到该方法进行操作。
如果用类调用类方法,那么系统回到类对象的 isa 指针指向的对象中操作。
在对象中找方法的时候,首先要在isa指针指向的对象中的缓存方法列表中找该方法,如果找到直接拿来用,反之如果没有找到,那么接着会到该对象的方法列表中找,如果找到直接拿来用,如果没有找到,那么就去对象的父类的isa指针中指向的对象中找,找的过程还是跟上面一样,先从缓存中找。如果一直都没有找到该方法,那么就要转到拦截调用方法了(NSobjct对象中的方法),如果没有实现拦截方法,那么程序就会报错。
总结出来就是,当调用方法的时候,会进行以下操作:
1、 从本身的isa指针所指对象的缓存方法中找,如果找到直接转向相应的方法实现,如果没有找到转向2
2、从对象的方法列表中找,如果找到直接用,如果没有找到转向3
3、从对象的父类的isa指针所指的对象的缓存方法中找,如果找到直接转向方法的实现,没有的话转向4
4、从父类isa指针所指对象的方法列表中找,如果找到直接转向方法的实现,没有的话转向5
5、到超父类还没有找到的话,就需要转到拦截调用啦。
6、如果拦截调用方法没用实现的话程序就要报错了。
拦截调用
拦截调用就是当没有找到调用的方法的时候会转向拦截调用(NSObjec对象中的方法)
拦截调用一般都是通过下面四个方法来实现的
-
- (id)forwardingTargetForSelector:(SEL)aSelector OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
这个方法是把你调用的不存在的方法重定向到一个其他声明了这个方法的泪,只需要你返回一个有这个方法的target
- - (void)forwardInvocation:(NSInvocation *)anInvocation OBJC_SWIFT_UNAVAILABLE("");
这个方法是把你调用的不存在的方法打包成NSInvocation传给你,做完自己的处理后调用 invokeWithTarget:方法让某个targer触发这个方法。
- + (BOOL)resolveClassMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
这个方法是当你调用一个不存在的类方法的时候,会调用这个方法,可以在这里加上自己的处理然后返回YES,这里默认是返回NO的;
-
+ (BOOL)resolveInstanceMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
这个方法是当你调用一个不存在的实例方法的时候触发的;
动态添加方法
当调用一个不存在的方法的时候,被拦截到后,如果不处理,仍然是没有改方法的,程序仍然会出错。
有一个方法是根据传进来的SEL的类型来动态的添加方法的。
4个参数分别表示
Class cls 给哪个类添加方法
SEL name 添加的方法
IMP imp 方法的实现,C方法的实现可以直接获取,OC的方法实现需要调用+(IMP)instanceMethodForSelector:(SEL)aSelector 来调用方法
“v@:*”方法的签名,代表有一个参数的方法
关联对象