OC是一门动态运行时的语言,为了能够高效运作,不仅需要一个编译器,也需要一个运行时系统来动态得创建类和对象、进行消息传递和转发。对runtime机制的理解,除了能深入了解系统的核心内容以外,也可以在项目中更好的去扩展以更高效的实现。
OC并不能直接编译为汇编语言,而是要先转写为纯C语言再进行编译和汇编的操作,从OC到C语言的过渡就是由runtime来实现的。然而我们使用OC进行面向对象开发,而C语言更多的是面向过程开发,这就需要将面向对象的类转变为面向过程的结构体。
因此理解runtime的原理不仅能帮助去学习runtime,也能去帮助我们更深入的了解OC对象的实现。接下来我们就来了解一下OC一些对象的组成和原理:
类的理解
objc_class 类对象
在OC中我们使用类,但是都是封装成类名,那么对象(object),类(class)底层是怎么封装的?
我们可以查找Class
的定义,可以看到它是个objc_class
类型的对象,而objc_class
则是C语言中的结构体。
typedef struct objc_class *Class;
struct objc_class {
Class _Nonnull isa;
#if !__OBJC2__
// 父类
Class _Nullable super_class;
// 类名
const char * _Nonnull name;
// 版本
long version;
// 其他信息
long info;
// 实例方法大小
long instance_size;
// 属性列表
struct objc_ivar_list * _Nullable ivars ;
// 方法列表
struct objc_method_list * _Nullable * _Nullable methodLists;
// 缓存对象
struct objc_cache * _Nonnull cache;
// 协议列表
struct objc_protocol_list * _Nullable protocols ;
#endif
} OBJC2_UNAVAILABLE;
类对象就是一个结构体,这个结构体存放的数据称为元数据(metadata
),,objc_class
中包含所定义的属性,方法,协议。同时还有为了优化运行时的效率而定义的缓存cache,所属的父类super_class的Class,以及该类的isa指针。
objc_object 实例对象
我们通过一个类创建一个对象,这个对象本身也是一个结构体objc_object
,该结构体是指向Class的isa指针:
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
说明类创建的对象本质是一个指向Class的指针对象。
isa指针和元类(Meta Class)
类对象中的元数据存储的都是如何创建一个实例的相关信息,那么类对象和类方法应该从哪里创建呢?就是从isa
指针指向的结构体创建,类对象的isa指针指向的我们称之为元类(metaclass)
,
objc_class
也是一个对象,在objc_object
和objc_class
中我们看到都定义了isa
指针,找到其继承通过isa
指针。objc_object
指向的是objc_class
,而objc_class
指向的元类类的Meta Class
同时通过super_class
指向的父类的super_class
。
这样的一个流程可以从子类对象根据isa
和superClass
层层找到元类(metaclass)
,因此isa
是指向元类
,通过superClass
指针指向父类,通过这种方法可以去建立层次关系。
例如,定义一个NSString对象:
NSString *str = @"name";
其通过isa指针和superClass参数建立的指向关系:
NSString对象->NSString类对象->NString元类->NSObject元类
NSString对象->NSString类对象->NSObject类对象(superClass)->通过isa指向NSObject元类

如上图所示,相对于
isa
对象指向Class
,Class
指向metaClass
;相对于superClass
, Class
指向superClass
。这样superClass
的isa
指向也supermetaClass
;最终指向rootMetaClass
。
objc_method_list
每个Class
都有对应的cache
和methodLists
,当调用方法时,通过这两者,找到对应的目标objc_method
执行过程。其中objc_method
包括:选择子、方法类型、IMP指针。objc_ cache
而缓存的目的是为了优化再次调用方法时,可以直接获取。
struct objc_method {
SEL method_name;
char * method_types;
IMP method_imp;
};
struct objc_method_list {
struct objc_method_list * obsolete;
int method_count;
#ifdef __LP64__
int space
#endif
struct objc_method method_list[1];
};
前篇讲过消息传递的过程,当对象调用消息时:
先判断接收者(receiver
)是否为nil
;
再通过对象isa
指针找到所属Class
;
查找对应的cache
,如果有存在该选择子(SEL
)的objc_method
对象,转发IMP
返回的值;
如果cache
不存在,去查找该Class
的methodLists
列表,找到存入cache
中,并执行IMP
;
如果methodLists
也不存在,通过super_class
查找父类,与前面一样查找父类的cache
和methodLists
如果都没有找到,就执行消息转发。
网友评论