Runtime之理解OC中类与对象的实质
在阅读Objective-C高级编程中block章节时,在第2章里讲到block的实质,作者写道,要理解block的实质,首先需要理解OC中类和对象的实质,在学习的过程中对这部分的理解有一些困扰,本文从源代码的角度对类和对象的本质进行学习。
关键点:类本身也是一种对象
没错,这句话我也理解了很久,首先来看OC中是如何定义的。
OC里的id
”id“这一变量类型用于存储OC对象,在OC源代码中,描述OC对象所用的数据结构定义在运行期程序库的头文件里,在/usr/include/objc/runtime.h中是如下进行声明的
typedef struct objc_object{
Class isa;
} *id;
可以看到,id是objc_object结构体的指针类型。Class的定义如下。
typedef struct objc_class *Class;
struct objc_class{
Class isa;
}
可以看到,Class是一个objc_class结构体的指针类型,而objc_class这个结构体里,又有一个Class的对象isa。
每个objc_object结构体的首个成员是Class类的变量,该变量定义了对象所属的类,通常称为“is a”指针。而objc_class结构体的首个变量也是isa指针,这说明Class本身亦为Objective-C对象。在OC中任何类定义都是对象,类对象在第一次使用时被创建,并且有且仅有一份实例。而这个类对象就是运行时库用来创建实例对象的依据。
类对象(class object)
任何直接继或间接继承了NSObject的类,它的实例对象中都有一个isa指针,指向它的类对象。这个类对象中存储了关于这个实例对象所属类的定义的一切,包括变量、方法、遵守的协议等。因此,类对象能访问关于这个类的所有信息,生成一个实例对象,但是不能访问任何实例对象的内容。
类对象所属的类型(也就是说isa指针所指向的类型)是另外一个类,叫做元类(metaclass),用来表述类对象本身所具备的元数据。“类方法”就定义于此处,因为这些方法可以理解成类对象的实例方法。当你调用“类方法”,例如[NSObject alloc]时,事实上是发送了一个消息给NSObject的类对象。
每个类仅有一个“类对象”,每个“类对象”仅有一个与之相关的“元类”。
类对象是一个功能完整的对象,能被动态识别(dynamically typed),接收消息,从其他类继承方法。特殊之处是它们是由编译器创建的,缺少它们自己的数据结构(实例变量),只是在运行时产生实例的代理。
元类对象(metaclass object)
类对象实际上是元类对象的一个实例。
元类对象描述了一个类对象,元类的方法列表是类方法的集合,由类对象的选择器来响应。当向一个类发送消息时,objc_msgSend会通过类对象的isa指针定位到元类,并检查元类及元类的父元类来决定调用那个方法。元类描述了类对象的方法,就像类对象描述了实例对象的实例方法。
假设有个名为SomeClass的子类从NSObject中继承而来,则其继承体系如下图所示。

super_class指针确立了继承关系,而isa指针描述了实例所属的类。
元类也是对象,是其他类的实例,在OC中,元类是根元类(root class's metaclass)的实例,而根元类是其自身的实例。
类与对象的实质
- 类本身是一种对象,一个类对象只有一份
- 类对象中包含了类的实例变量、实例方法的定义
- 元类对象包括了类的类方法的定义
- 根元类的isa指向根元类自身
- 根元类对象生成元类对象,元类对象生成类对象,类对象生成实例对象
本文参考
- Objective-C高级编程
- Effective Objective-C 2.0
- https://blog.csdn.net/wzzvictory/article/details/8592492
- https://www.jianshu.com/p/0a1c4879a2e3