Runtime机制
一.runtime的源码部分解读
首先贴上Runtime代码,参照着代码进行runtime的学习
Runtime代码 (使用版本为objc4-646.tar)
这里,我们先了解一下Runtime里面包含了一些什么,这里我参考了一下简书里面的一篇比较不错的文章.已经各大论坛里面详细的文章
当我们调用一个函数的时候,编译器会转化为
objc_msgSend(receiver,message);
//假如消息中含有参数,会转化为如下形式:
[receiver message:arg...];
objc_msgSend(receiver,message,arg1,arg2,...);
1.id到底是什么
id objc_msgSend(id self,SEL op...)
那这里又会给出id的概念
typedef struct objc_object *id;
2.objc_object是什么
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};
objc_object含有isa指针,isa指针的作用就是找到对象的所属的类
这里参考简书中提到的isa指针注意的东西,isa指针在代码运行的时候并不总指向实例所属的类,所以不能单单依靠isa来确定类型,可以通过 class方法来确定。因为在KVO实现原理中,isa指针指向的是一个该类(基类)的派生类(补充kvo的实现原理:当你第一次观察某个对象的时候,runtime会创建一个基于该类的派生类,然后将isa指针指向这个派生类,在这个派生类中重写了被观察属性的setter方法,当一个属性的值改变时,在重写的setter 方法里面来实现通知机制,此时的isa指针指向的是派生类而不是基类)
3.Class类型是什么
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE;
const char *name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE;
struct objc_method_list **methodLists OBJC2_UNAVAILABLE;
struct objc_cache *cache OBJC2_UNAVAILABLE;
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
它里面还有一个isa指针,这里因为Objective-C本身也是对象,为了区别对象和类,Objc创建了Meta Class的东西,类对象所属的类叫做元类。
我们熟悉的类方法,就来自于元类,可以理解为类方法就是类对象的实例方法,每一个类只有一个类对象,每一个类对象只有一个类,当发出一个[NSObject new]的消息时,事实上是把这个类发送给了类对象,这个类对象是元类的一个实例,而这个元类也是一个根元类(root meta class),所有的元类最终都指向根元类为自身的父类。所有的元类的方法列表中都有能够响应消息的方法,所以当[NSObject new]消息发送给类对象的时候,objc_msgSend就会去方法列表中找到相应的方法,找到了就去响应
4.Cache是什么
Cache为方法调用的性能进行优化,也就是在方法调用的时候会首先从这个缓存列表中找,如果找到就执行,没有找到就通过isa指针在类的方法列表中寻找,找到之后执行并把这个方法添加到缓存列表里,以供下一次调用,当下一次调用的时候直接从缓存列表中找,这样提高了性能,不用通过isa指针去查找。
它有指向超类的super_class指针.其中Objc_ivar_list是成员变量objc_ivar列表; objc_method_list是objc_method方法列表
其中的属性具体这里不做介绍了
二.消息转发机制
1.过程
首先,如果要了解Runtime机制,我们不得不先说一下iOS的消息转发机制,来看下面的这一段代码
- (void)testMessageSend {
UILabel *label = [[UILabel alloc] initWithFrame:self.view.bounds];
label.backgroundColor = [UIColor redColor];
[self.view addSubview:label];
[self performSelector:@selector(setTextColor:) withObject:nil afterDelay:1];
}
我们写这么一个测试方法 因为是在控制器下调用,而控制器肯定不会有setTextColor这个方法,所以,正常情况下,运行该方法后,肯定会导致系统奔溃,我们来看一下奔溃日志
[ViewController setTextColor:]: unrecognized selector sent to instance 0x7fdd1a508040'
打印错误的结果,很显然,没有找到该方法
原因是因为消息发送给了不能处理它的对象,然而有时我们并不希望出现这种奔溃的现象,那么它在这一系列工程中发生了一下什么,如何使其不奔溃呢
首先,编译器将代码[self setTextColor ] 转化为objc_msgSend(obj,@selector(setTextColor)); 在objc_msgSend函数中,首先通过obj的isa指针找到obj对应的class.在Class中先去cache中通过SEL查找对象函数method,若cache中为找到到,再去methodList中查找,若methodlist中未找到,则去superClass中查找。若能找到,则将method加入到cache中,方便下次查找,并通过method中的函数指针跳转到对应的函数中去执行。
当在当前类和父类中都找不到对应的方法实现时,则
1.首先对象在收到无法解读的消息后,首先将调用其所属的下列类方法
+ (BOOL)resolveInstanceMethod:(SEL)sel
参数为SEL类型的对象,是一个方法选择器,返回值为BOOL类型,表示这个类是否能新增一个实例来处理选择器的方法。继续往下执行级之前,本类有机会新增一个处理此选择器的方法。加入尚未实现的方法不是实例方法而是类方法,那么运行期系统就会调用下一个方法
+ (BOOL)resolveClassMethod:(SEL)sel
如果不想让程序cash掉,那么我们把要实现的代码写好,等着运行的时候动态的插在类里面就可以了。此方案一般用来实现@dynamic属性
2.如果在这里没有得到处理,那么接收者还有第二次机会能处理位置的方法,这一步中,运行期系统会问它:能不能把这条消息转给其他接收者来处理,对应所执行的方法如下
- (id)forwardingTargetForSelector:(SEL)aSelector
方法的参数表示未知的选择子,若当前接收者能找到被援对象,则将其返回,若找不到就返回nil。在一个对象内部,可能还有一系列其他的对象,该对象可经由此方法能够处理某选择字的相关内部返回,这样,在外界看来,好像是对象亲自处理了这些消息似得。
3.最后的不久措施,系统唯一能做的就是启用完整的消息转发机制了。首先创建NSInvocation对象,把与尚未处理的那条消息有关的全部细节都封于其中。在出发NSInvocation对象时,“消息派发系统”将亲自出马,把消息指派给目标对象,方法如下
-(void)forwardInvocation:(NSInvocation *)invocation
这个方法实现很简单:只需改变调用目标,使消息在新目标上得以调用即可。然而这样实现出来的方法与"备援接收者"方案实现的法等效,所以很少有人采用这么简单的实现放啊是。
如果在这几步里面做任何操作,那么将会奔溃
参考文章:http://www.jianshu.com/p/ea1743715609