本文转自:http://www.cocoachina.com/ios/20141018/9960.htmlhtml
最近在找工做,Objective-C中的Runtime是常常被问到的一个问题,几乎是面试大公司必问的一个问题。固然还有一些其余问题也几乎必问,例 如:RunLoop,Block,内存管理等。其余的问题若是有机会我会在其余文章中介绍。本篇文章主要介绍RunTime。ios
RunTime简称运行时。就是系统在运行的时候的一些机制,其中最主要的是消息机制。对于C语言,函数的调用在编译的时候会决定调用哪一个函数( C语言的函数调用请看这里 )。编译完成以后直接顺序执行,无任何二义性。OC的函数调用成为消息发送。属于动态调用过程。在编译的时候并不能决定真正调用哪一个函数(事实证实,在编 译阶段,OC能够调用任何函数,即便这个函数并未实现,只要申明过就不会报错。而C语言在编译阶段就会报错)。只有在真正运行的时候才会根据函数的名称找 到对应的函数来调用。面试
那OC是怎么实现动态调用的呢?下面咱们来看看OC经过发送消息来达到动态调用的秘密。假如在OC中写了这样的一个代码:函数
1
|
[obj makeText];
|
其中obj是一个对象,makeText是一个函数名称。对于这样一个简单的调用。在编译时RunTime会将上述代码转化成oop
1
|
objc_msgSend(obj,@selector(makeText));
|
首先咱们来看看obj这个对象,iOS中的obj都继承于NSObject。spa
1
2
3
|
@interface NSObject <nsobject> {
Class isa OBJC_ISA_AVAILABILITY;
}</nsobject>
|
在NSObjcet中存在一个Class的isa指针。而后咱们看看Class:指针
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
typedef struct objc_class *Class;
struct objc_class {
Class isa;
// 指向metaclass
Class super_class ;
// 指向其父类
const char *name ;
// 类名
long version ;
// 类的版本信息,初始化默认为0,能够经过runtime函数class_setVersion和class_getVersion进行修改、读取
long info;
// 一些标识信息,如CLS_CLASS (0x1L) 表示该类为普通 class ,其中包含对象方法和成员变量;CLS_META (0x2L) 表示该类为 metaclass,其中包含类方法;
long instance_size ;
// 该类的实例变量大小(包括从父类继承下来的实例变量);
struct objc_ivar_list *ivars;
// 用于存储每一个成员变量的地址
struct objc_method_list **methodLists ;
// 与 info 的一些标志位有关,如CLS_CLASS (0x1L),则存储对象方法,如CLS_META (0x2L),则存储类方法;
struct objc_cache *cache;
// 指向最近使用的方法的指针,用于提高效率;
struct objc_protocol_list *protocols;
// 存储该类遵照的协议
}
|
咱们能够看到,对于一个Class类中,存在不少东西,下面我来一一解释一下:code
Class isa:指向metaclass,也就是静态的Class。通常一个Obj对象中的isa会指向普通的Class,这个Class中存储普通成员变量和对 象方法(“-”开头的方法),普通Class中的isa指针指向静态Class,静态Class中存储static类型成员变量和类方法(“+”开头的方 法)。htm
Class super_class:指向父类,若是这个类是根类,则为NULL。对象
下面一张图片很好的描述了类和对象的继承关系:
注意:全部metaclass中isa指针都指向跟metaclass。而跟metaclass则指向自身。Root metaclass是经过继承Root class产生的。与root class结构体成员一致,也就是前面提到的结构。不一样的是Root metaclass的isa指针指向自身。
Class类中其余的成员这里就先不作过多解释了,下面咱们来看看:
@selector (makeText):这是一个SEL方法选择器。SEL其主要做用是快速的经过方法名字(makeText)查找到对应方法的函数指针,而后调用其函 数。SEL其自己是一个Int类型的一个地址,地址中存放着方法的名字。对于一个类中。每个方法对应着一个SEL。因此iOS类中不能存在2个名称相同 的方法,即便参数类型不一样,由于SEL是根据方法名字生成的,相同的方法名称只能对应一个SEL。
下面咱们就来看看具体消息发送以后是怎么来动态查找对应的方法的。
首先,编译器将代码[obj makeText];转化为objc_msgSend(obj, @selector (makeText));,在objc_msgSend函数中。首先经过obj的isa指针找到obj对应的class。在Class中先去cache中 经过SEL查找对应函数method(猜想cache中method列表是以SEL为key经过hash表来存储的,这样能提升函数查找速度),若 cache中未找到。再去methodList中查找,若methodlist中未找到,则取superClass中查找。若能找到,则将method加 入到cache中,以方便下次查找,并经过method中的函数指针跳转到对应的函数中去执行。