该文中所有的结构体定义都可以在runtime.h文件中找到
Object
先上一张图,然后围绕这个图开始介绍
OC是基于C语言的一个超类,OC里类的概念和java,c++等语言的类在语法上很相似,但是实际在底层执行的时候并不一样,因为C语言中没有类这个概念,而所有的OC语言,最终在执行的时候都是转换成C语言来执行,那么问题来了,OC中的Object究竟是一个什么概念,它在C语言层面是一个什么样的形式?
图的左边是一个OC的实例,这个实例中有一个isa指针,指针是Class类型,这个指针指向的就是这个实例的定义。Class指针在内部被定义为:
typedef struct objc_class *Class
object_class是用C语言定义的一个结构体,这个就是OC中的Object真实的面目。图右显示了该结构体组成,下面开始逐个解释每个属性的作用:
1.Class isa
实例的isa指针是指向这个实例的类结构体,相当于是元类(meta-class)。而每个类结构体中也有isa指针,此时可以在将这个类结构体看成一个实例,它的isa指针也是指向这个类的元类。看下面的代码:
@interface MyController:ViewController
-(void)func1;
-(void)func2;
+(void)func3;
+(void)func4;
...
...
MyViewController *myVC = [MyViewController alloc] init];
上面代码定义个了一个MyController的类,包含两个实例方法(func1,func2)和两个类方法(func3,func4)。后面定义了一个myVC实例,此时myVC中的isa指针指向的就是MyController的objc_class结构体,但是此时这个结构体中的方法列表是不包含func3和func4这两个类方法的,MyController结构体中的isa指针指向MyController的元类(meta-class)结构体,这个meta-class结构体中的方法列表包含了func3和func4这两个类方法。这里有一个问题,每一个meta-class本身也是可以看做一个实例,可以向它发送消息,那它的isa指针又指向谁呢?为了避免无限延伸,所有的meta-class结构体中的isa指针最终都会指向NSObject,而NSObjec中的isa指针则指向自己,形成一个回路。
2.Class super_class
super_class从字面很容易理解,就是指向父类的结构体,最终super _class指针会指向root class(NSObject或者NSProxy),root class的super _class指针为nil。
借用南峰子的技术博客上的一张图,图示isa和super_class指针:
上图的class,meta class,super class和super meta class的关系可以在runtimeobjc-runtime-new.mm
的代码中找到:
每当创建一个新的class的时候,会调用objc_allocateClassPair
方法
// Connect to superclasses and metaclasses
cls->initClassIsa(meta);
if (superclass) {
meta->initClassIsa(superclass->ISA()->ISA());
cls->superclass = superclass;
meta->superclass = superclass->ISA();
addSubclass(superclass, cls);
addSubclass(superclass->ISA(), meta);
} else {
meta->initClassIsa(meta);
cls->superclass = Nil;
meta->superclass = cls;
addSubclass(cls, meta);
}
3.const char *name
类名sdsds
4.long version
5.long info
6.long instance_size
7.struct objc_ ivar_ list *ivars
类中定义的变量都存在这个列表里。
8.struct objc _method _list **methodLists
类中定义的方法都存放在这个列表里,该列表的会在后面详细介绍。
9.struct objc_cache *cache
这是一个缓存列表,用于缓存那些已经被调用过的方法,当再次访问同一个方法就会到这个列表中去查找,加快了方法的访问速度。
10.struct objc_protocol _list *protocols
保存该类要实现的protocol。
Message
在介绍这一节之前,首先明确一下method和message的区别:
在java或c++中,method是一段代码,这段代码与某一个Class相关联。
在OC中,从语法层面来看,调用类中的方法看起来和java这种语言很类似,实际在调用一个方法时,是向Class发送了一条消息。然后由这个Class去处理消息。
[obj fun:@"hello"]; // OC语法
objc_msgSend(obj,@selector(fun:),@"hello"); //实际触发的方法。
从上面代码可以看到,在OC中调用方法的代码,都会被转换成objc_msgSend(id,SEL,…)这种形式,其中第一个参数是Object,就是接收消息的类,第二个参数是方法名,后面的参数是方法中的参数。
了解了在OC中调用方法的基本概念后,来看一下Method的定义:
前面在讲Object的结构的时候,提到存储方法的列表是一个叫做objc_method _list的结构体,如上图所示,这个结构体有四个属性。
在这个结构体中, 还有一个方法列表objc_method 这个列表才是真正存储方法的列表,从图右可以看到这个结构体的定义。分别记录了方法名,方法的返回类型以及IMP,IMP是方法的具体执行代码的地址,也可以叫做函数指针。
typedef id (*IMP)(id, SEL, ...);
现在来理一遍当执行一个方法的流程:当objc_msgSend被执行时,首先通过isa指针找到struct objc _class,然后定位到objc _method _list,通过查找obj _method 列表中method _name,如果找到,则根据IMP来执行,否则在去super _class中查找。
这里有一个问题,如果每次都要从objc_method _list中去线性超找方法名效率会比较低,尤其是含有大量方法的时候。查找会很慢,为了解决这个问题。objc _class结构体中,有一个struct objc _cache *cache的列表,当第一次执行某一个方法时,会在objc _method _list中找,然后这个方法会被记录在cache列表中,下次再执行时则直接从cache中去查找。下面看一下struct objc _cache的定义:
struct objc_cache {
unsigned int mask /* total = mask + 1 */;
unsigned int occupied;
Method buckets[1];
}
该结构体定义了一个hash table存储在Method中(Method也是一个objc_method指针),其中selector被当作key。
如果有任何问题欢迎再下面留言,或者扫描二维码