Objective-C特性:Runtime

566 查看

Objective-C是基于C语言加入了面向对象特性和消息转发机制的动态语言,这意味着它不仅需要一个编译器,还需要Runtime系统来动态创建类和对象,进行消息发送和转发。下面通过分析Apple开源的Runtime代码(我使用的版本是objc4-646.tar)来深入理解Objective-C的Runtime机制。

Runtime数据结构
在Objective-C中,使用[receiver message]语法并不会马上执行receiver对象的message方法的代码,而是向receiver发送一条message消息,这条消息可能由receiver来处理,也可能由转发给其他对象来处理,也有可能假装没有接收到这条消息而没有处理。其实[receiver message]被编译器转化为:

下面从两个数据结构id和SEL来逐步分析和理解Runtime有哪些重要的数据结构。

SEL
SEL是函数objc_msgSend第二个参数的数据类型,表示方法选择器,按下面路径打开objc.h文件


查看到SEL数据结构如下:

其实它就是映射到方法的C字符串,你可以通过Objc编译器命令@selector()或者Runtime系统的sel_registerName函数来获取一个SEL类型的方法选择器。
如果你知道selector对应的方法名是什么,可以通过NSString* NSStringFromSelector(SEL aSelector)方法将SEL转化为字符串,再用NSLog打印。

id
接下来看objc_msgSend第一个参数的数据类型id,id是通用类型指针,能够表示任何对象。按下面路径打开objc.h文件

查看到id数据结构如下:

id其实就是一个指向objc_object结构体指针,它包含一个Class isa成员,根据isa指针就可以顺藤摸瓜找到对象所属的类。

注意:根据Apple的官方文档Key-Value Observing Implementation Details提及,key-value observing是使用isa-swizzling的技术实现的,isa指针在运行时被修改,指向一个中间类而不是真正的类。所以,你不应该使用isa指针来确定类的关系,而是使用class方法来确定实例对象的类。

Class
isa指针的数据类型是Class,Class表示对象所属的类,按下面路径打开objc.h文件


Class Data Structure

可以查看到Class其实就是一个objc_class结构体指针,但这个头文件找不到它的定义,需要在runtime.h才能找到objc_class结构体的定义。

按下面路径打开runtime.h文件


objc_class Data Structure
查看到objc_class结构体定义如下:

注意:OBJC2_UNAVAILABLE是一个Apple对Objc系统运行版本进行约束的宏定义,主要为了兼容非Objective-C 2.0的遗留版本,但我们仍能从中获取一些有用信息。

让我们分析一些重要的成员变量表示什么意思和对应使用哪些数据结构。

isa表示一个Class对象的Class,也就是Meta Class。在面向对象设计中,一切都是对象,Class在设计中本身也是一个对象。我们会在objc-runtime-new.h文件找到证据,发现objc_class有以下定义:

由此可见,结构体objc_class也是继承objc_object,说明Class在设计中本身也是一个对象。

其实Meta Class也是一个Class,那么它也跟其他Class一样有自己的isa和super_class指针,关系如下:

上图实线是super_class指针,虚线是isa指针。有几个关键点需要解释以下:

  • Root class (class)其实就是NSObject,NSObject是没有超类的,所以Root class(class)的superclass指向nil。
  • 每个Class都有一个isa指针指向唯一的Meta class
  • Root class(meta)的superclass指向Root class(class),也就是NSObject,形成一个回路。
  • 每个Meta class的isa指针都指向Root class (meta)。
  • super_class表示实例对象对应的父类
  • name表示类名
  • ivars表示多个成员变量,它指向objc_ivar_list结构体。在runtime.h可以看到它的定义: