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元类
class关系图.png
如上图所示,相对于
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如果都没有找到,就执行消息转发。













网友评论