转自:http://www.cocoachina.com/ios/20141218/10679.html
0. Objective-C与C
从Objective-C的名字上我们就能看出来它和C语言有着很密切的关系,是“面向对象的C语言”。Objective-C诞生于上世纪80年代,那个时候面向对象的理念已经得到了初步发展,C语言当时也已经是很成熟的语言。和C++类似,Objective-C也在C语言的基础上,增添了面向对象的特性。同是在1983年前后出现,Objective-C和C++在对C语言的面向对象特性扩展上,走出了不同的道路。
Objective-C语言的实现用到了一个用C语言写的运行时系统,也就是一个动态链接库——libobjc.A.dylib。这个动态库里面提供了Objective-C语言所需的各种动态特性,包括支持面向对象的一些特性。而Objective-C的编译器可以以C编译器为基础,对Objective-C的语法做“预处理”扩展(GCC里的Objective-C编译器就是这么做的)。
所以可以说Objective-C是以C语言为基础,通过引入特有的运行时库,形成一门支持面向对象特性的语言。
1. Objective-C目标文件中的运行时信息
前一篇文章中,我们通过一段示例代码演示了运行时对面向对象中多态特性的支持。一个消息发送语句会根据运行时的信息判断,具体应该调用哪个函数实现(比如区分父类还是子类)。而这些运行时所需要的信息最终又是开发人员在写代码时就添加进去的,其中蕴含着特定的逻辑。
其实,可以说不仅仅是Objective-C,绝大多数面向对象语言都是类似的。想要支持运行时多态,代码中一定包含有运行时所需的判断数据,而且经过编译器编译过后一定会保留下来。只有这样,机器在执行时才能有所依据。
像Java的JVM,在运行时为Java程序提供了强大的支持,而Java的运行时信息都在.class文件中按规范格式保留下来。编译后的C++代码也一样保有RTTI。
不像Java的虚拟机那样重,能够支持python等其它语言生成的class。Objective-C的运行时针对Objective-C语言,更加短小精悍、简单实用。而Objective-C代码中的运行时信息都保留在编译过后的目标文件中(.o)。
实用Objective-C的编译器编译.m文件之后,我们会看到生成有.o目标文件,目标文件中包含头部、加载指令和各个段。其中有一个segment专门负责保留Objective-C的运行时信息。
比如,实用otool,我们可以看到其中的内容:
如上图中所示,这个.o目标文件中包含了一个类(class)和一个元类(meta class)的信息。这其中包括实例的大小、类名、父类、方法列表即使地址、protocal起始地址等。
如果,你和我一样,之前做过Java,并且对Java的class文件格式很感兴趣,那么现在你会发现,这个目标文件中包含的Objective-C运行时信息和class中的数据内容极为相似。
是的,技术方案是互通的,是相似的,相信其他很多面向对象的语言也有类似的方式。
2. 对象模型
刚刚提到了类和元类,这些是什么? 在Objective-C中一个对象又是怎样的实现? 什么是对象? 相信读完本段对Objective-C对象模型的介绍,如上问题就会自然解开了。
首先我们相信,无论是类还是对象,在最终的实现中都是一种数据结构,这点应该没什么好质疑的。
我们来看看Objective-C的对象是个什么东西?
1
2
3
4
|
typedef struct objc_class *Class;
typedef struct objc_object {
Class isa;
} *id;
|
从objc.h中的如上代码来看,Objective-C的对象就是一个包含isa指针的数据结构,而isa又是一个Class类型的,Class则是一个名为objc_class的数据结构定义。
再看runtime.h中对objc_class的定义:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
struct objc_class {
Class 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;
|
其中也是一个包含isa的结构。咦!那objc_object和objc_class不就一样了?实际上看到这里,的确就是这样。
当然,一个实际的类,里面还会包含各个变量成员,所以类定义好了,category也不能直接增加变量存储。而这个isa就指向这个对象的实际类(isa指向一个“类对象”)。正是因为这样,运行时一个消息发送才知道具体应该调用哪个方法实现。同时,一个类又包含了这个类的所有信息(在“类对象”结构中记载)。这个“类对象”的isa指向元类(meta class)对象。“类对象”中有方法列表,这里面的方法是实例方法(减号“-”方法),而“元类对象”中的方法列表则是类方法(加号“+”方法)。
我们来看张对象的图:
有没有觉得这个结构很熟悉? 没错,其实debug的时候都能看到,只是不是以图像的形式展现出来而已。
而关于“类”和“元类”,我们再看另一张图:
看完上面这张Objective-C对象模型的关系图,似乎一切就都清楚了!
就整理到此,希望能够帮助各位朋友们读懂Objective-C运行时中的奥秘。