如果你曾经对ObjC底层的实现有一定的了解,你应该会知道Objective-C对象都是 C 语言结构体,所有的对象都包含一个类型为 isa 的指针,那么你可能确实对 ObjC 的底层有所知,不过现在的 ObjC 对象的结构已经不是这样了。代替 isa 指针的是结构体 isa_t,这个结构体中“包含”了当前对象指向的类的信息,这篇文章中会介绍一些关于这个变化的知识。
struct objc_object {
isa_t isa;
};
当 ObjC 为一个对象分配 内存,初始化实例变量后,在这些对象的实例变量的结构体中的第一个就是 isa。
所有继承自 NSObject 的类实例化后的对象都会包含一个类型 isa_t 的结构体。
从上图中可以看出,不只是实例会包含一个 isa 结构体,所有的类也有这么一个 isa。在 ObjC 中 Class 的定义也是一个名为 objc_class 的结构体,如下:
struct objc_class : objc_object {
isa_t isa;
Class superclass;
cache_t cache;
class_data_bits_t bits;
};
由于 objc_class 结构体是继承自 objc_object 的,所以在这里显式地写出了 isa_t isa 这个成员变量。
isa 指针的作用与元类
到这里,我们就明白了:Objective-C 中类也是一个对象。
这个 isa 包含了什么呢?回答这个问题之前,要引入了另一个概念 元类(meta class),我们先了解一些元类的信息。
因为在 Objective-C 中,对象的方法并没有存储于对象的结构体中(如果每一个对象都保存了自己能执行的方法,那么对内存的占用有极大的影响)。
当实例方法被调用时,它要通过自己持有的 isa 来查找对应的类,然后在这里的 class_data_bits_t 结构体中查找对应方法的实现。同时,每一个 objc_class 也有一个指向自己的父类的指针 super_class 用来查找继承的方法。
关于如何在 class_data_bits_t 中查找对应方法会在之后的文章中讲到。这里只需要知道,它会在这个结构体中查找到对应方法的实现就可以了。
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class super_class
const char *name
long version
long info
long instance_size
struct objc_ivar_list *ivars
struct objc_method_list **methodLists // 方法定义的链表
struct objc_cache *cache // 方法缓存,对象接到一个消息会根据isa指针查找消息对象
struct objc_protocol_list *protocols // 协议链表
#endif
} OBJC2_UNAVAILABLE;
/* Use `Class` instead of `struct objc_class *` */
isa:是一个Class 类型的指针. 每个实例对象有个isa的指针,他指向对象的类,而Class里也有个isa的指针, 指向meteClass(元类)。元类保存了类方法的列表。当类方法被调用时,先会从本身查找类方法的实现,如果没有,元类会向他父类查找该方法。同时注意的是:元类(meteClass)也是类,它也是对象。元类也有isa指针,它的isa指针最终指向的是一个根元类(root meteClass).根元类的isa指针指向本身,这样形成了一个封闭的内循环。
super_class:父类,如果该类已经是最顶层的根类,那么它为NULL。
version:类的版本信息,默认为0
info:供运行期使用的一些位标识。
instance_size:该类的实例变量大小
ivars:成员变量的数组
每一个对象本质上都是一个类的实例。其中类定义了成员变量和成员方法的列表。对象通过对象的isa指针指向类。
每一个类本质上都是一个对象,类其实是元类(meteClass)的实例。元类定义了类方法的列表。类通过类的isa指针指向元类。
所有的元类最终继承一个根元类,根元类isa指针指向本身,形成一个封闭的内循环。