OC的方法调用的本质是runtime底层调用objc_msgSend,下面我们来分析一下objc_msgSend
的具体实现
1、创建一个Person类,有一个对象方法eat和一个实例方法run,并分别调用
@interface Person : NSObject
+(void)eat;//类方法
-(void)run;//实例方法
@end
[Person eat];
Person *p = [Person new];
[p run];
每个对象都会有一个它所属的类。这是面向对象的基本概念,但是在OC中,这对所有数据结构有效。任何数据结构,只要在恰当的位置具有一个指针指向一个class,那么,它都可以被认为是一个对象。
在OC中,一个对象所属于哪个类,是由它的isa指针指向的。这个isa指针指向这个对象所属的class。
实际上,OC中对象的定义是如下的样子:
typedef struct objc_object {
Class isa;
}*id;
这个定义表明:任何以一个指向Class的指针作为首个成员的数据结构都可以被认为是一个objc_object.
最重要的特性就是,你可以向OC中的任何对象发送消息
运行原理就是,当你向一个OC对象发送消息时,运行时库会根据对象的isa指针找到这个对象所属的类.这个类会包含一个所有实例方法的列表及一个指向superclass的指针以便可以找到父类的实例方法。运行时库会在类的方法列表以及父类(们)的方法列表中寻找符合这个selector的方法,找到后即运行这个方法。关键点就是类要定义这个你发送给对象的消息。
2、类在Runtime中的结构
struct objc_class
{
Class isa OBJC_ISA_AVAILABILITY;
//isa指针
//实例的isa指向类对象,类对象的isa指向元类
#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;
从上图可以知道Person类本身其实也是一个对象(p对象的isa指针指向Person类,Person类的isa指针指向它的元类meta class)
2、方法的调用分为类方法和实例方法
2.1实例方法的调用过程( 当你向一个对象发送消息时)
1.)在对象所属类的<缓存方法列表> 中去找要调用的方法,找到直接执行其实现。
2.)对象所属类的的<缓存方法列表> 里没找到,就去<类的方法列表>里找,找到了就执行其实现。
3.)还没找到,说明这个类自己没有了,就会通过super_class去向其父类里执行1、2。
4.)如果找到了根类还没找到,那么就是没有了,会转向一个拦截调用的方法,可以自己在拦截调用方法里面做一些处理。
5.)如果没有在拦截调用里做处理,那么就会报错崩溃。
2.2类方法的调用过程( 当你向一个类发送消息时)
1.)在元类meta class的<缓存方法列表> 中去找要调用的方法,找到直接执行其实现。
2.)在元类meta class<缓存方法列表> 里没找到,就去<元类的方法列表>里找,找到了就执行其实现。
3.)还没找到,说明这个类自己没有了,就会通过isa去meta类的父类里执行1、2。
4.)如果找到了根类还没找到,那么就是没有了,会转向一个拦截调用的方法,可以自己在拦截调用方法里面做一些处理。
5.)如果没有在拦截调用里做处理,那么就会报错崩溃。

3、这里简单讲一下meta class元类
meta-class,就像Class一样,也是一个对象。你依旧可以向它发送消息调用函数,自然的,meta-class也会有一个isa指针指向其所属类。所有的meta-class使用基类的meta-class作为他们的所属类。具体而言,任何NSObject继承体系下的meta-class都使用NSObject的meta-class作为自己所属的类。
根据这个规则,所有的meta-class使用基类的meta-class作为它们的类,而基类的meta-class也是属于它自己,也就是说基类的meta-class的isa指针指向它自己。(译:完美的闭环)
4、动态方法解析
当本类包括父类直到基类cache包括方法列表class_rw_t中都找不到方法时,就会进入动态方法解析阶段;
动态解析对象方法时,会调用+(BOOL)resolveInstanceMethod:(SEL)sel方法。
动态解析类方法时,会调用+(BOOL)resolveClassMethod:(SEL)sel方法。
5、消息快速转发
当本类没有实现方法,并且没有动态解析方法,就会调用forwardingTargetForSelector函数,进行消息转发,我们可以实现forwardingTargetForSelector函数,在其内部将消息转发给可以实现此方法的对象。
如果forwardingTargetForSelector函数返回为nil或者没有实现的话,就会调用methodSignatureForSelector方法,用来返回一个方法签名,这也是我们正确跳转方法的最后机会。
如果methodSignatureForSelector方法返回正确的方法签名就会调用forwardInvocation方法,forwardInvocation方法内提供一个NSInvocation类型的参数,NSInvocation封装了一个方法的调用,包括方法的调用者,方法名,以及方法的参数。在forwardInvocation函数内修改方法调用对象即可。
如果methodSignatureForSelector返回的为nil,就会来到doseNotRecognizeSelector:方法内部,程序crash提示无法识别选择器unrecognized selector sent to instance。