Objective-C 是一门面向对象的程序设计语言,它的对象模型是基于类来建立的。我们可以在这里下载最新的runtime源码,当前最新版本为objc4-709.tar.gz
Objective-C中的对象
NSObject
是Objective-C中的基类,NSObject
定义在objc4-709/runtime/NSObject.h
中:
@interface NSObject {
Class isa OBJC_ISA_AVAILABILITY;
}
在NSObject
的定义中,我们发现 NSObject
只有一个Class
类型的成员变量,同样在objc4-709/runtime/objc-private.h
中可以找到Class
的定义:
typedef struct objc_class *Class;
Class
其实也是一个结构体,也就是说Objective-C中的类其实也是一个结构体。继续找下去:
在 objc4-709/runtime/objc-runtime-new
中,能看到objc_class的定义:
struct objc_class : objc_object {
// Class ISA;
Class superclass;
cache_t cache; // formerly cache pointer and vtable
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
...
...
...
}
objc_class
是继承自objc_object
的,所以Objective-C中的类,其实也是一个对象。
在objc4-709/runtime/objc-private.h
中可以找到objc_object
对象的定义:
struct objc_object {
private:
isa_t isa;
public:
Class ISA();
Class getIsa();
void initIsa(Class cls /*nonpointer=false*/);
void initClassIsa(Class cls /*nonpointer=maybe*/);
void initProtocolIsa(Class cls /*nonpointer=maybe*/);
void initInstanceIsa(Class cls, bool hasCxxDtor);
Class changeIsa(Class newCls);
...
...
...
};
在Objective-C 中最基本的数据结构就是:struct objc_object
Objective-C中的对象本质上是一个结构体
id类型
在同一个文件中,我们还能找到Objective-C中id
的定义:
typedef struct objc_object *id;
能看到, id
其实就是一个 struct objc_object
类型的指针。但是 id
不是 NSObject *
,为什么可以指向任意的 NSObject
对象呢?因为 objc_object
只有一个 Class
类型的 isa
变量,而这个结构,恰恰和 NSObject
的定义一致。
这样,NSObject
对象的首地址处的内容,就能用id指向了。因为本质上,对象的数据都是由struct
存放的。
Class
对象的类不仅描述了对象的数据:对象占用的内存大小、成员变量的类型和布局等,而且也描述了对象的行为:对象能够响应的消息、实现的实例方法等。
在 Objective-C 中有一个非常特殊的类 NSObject ,绝大部分的类都继承自它。它是 Objective-C 中的两个根类(rootclass)之一,另外一个是 NSProxy。同样的,我们打开文件 NSObject.h ,可以看到 NSObject 类其实就只有一个成员变量 isa ,所有继承自 NSObject 的类也都会有这个成员变量。
如果把类的实例看成一个 C 语言的结构体(struct),上面说的 isa 指针就是这个结构体的第一个成员变量,而类的其它成员变量依次排列在结构体中。排列顺序如下图所示:
对象的实例方法调用时,通过对象的 isa 在类中获取方法的实现。
类对象的类方法调用时,通过类的 isa 在元类中获取方法的实现。
当一个对象的实例方法被调用的时候,会通过isa找到相应的类,然后在该类的class_data_bits_t中去查找方法。class_data_bits_t是指向了类对象的数据区域。在该数据区域内查找相应方法的对应实现。
元类(meta-class)之所以重要,是因为它存储着一个类的所有类方法。每个类都会有一个单独的meta-class,因为每个类的类方法基本不可能完全相同。
元类(metaclass)
因为类也是一个对象,那么类也必须是另一个类的实例,有点绕,可以理解成Class
是元类的对象。元类保存了类方法的列表。当一个类的方法被调用时,元类会首先查找它本身是否有该类方法的实现,如果没有,则该元类会向它的父类查找该方法,直到一直找到继承链的头。
元类 (metaclass) 也是一个对象,那么元类的 isa 指针又指向哪里呢?为了设计上的完整,所有的元类的 isa 指针都会指向一个根元类 (root metaclass)。根元类 (root metaclass) 本身的 isa 指针指向自己,这样就行成了一个闭环。上面提到,一个对象能够接收的消息列表是保存在它所对应的类中的。在实际编程中,我们几乎不会遇到向元类发消息的情况,那它的 isa 指针在实际上很少用到。不过这么设计保证了面向对象的干净,即所有事物都是对象,都有 isa 指针。
对应关系的图如下图,下图很好的描述了对象,类,元类之间的关系:
(图片源自 这里)
我们再来看看继承关系,由于类方法的定义是保存在元类 (metaclass
) 中,而方法调用的规则是,如果该类没有一个方法的实现,则向它的父类继续查找。所以,为了保证父类的类方法可以在子类中可以被调用,所以子类的元类会继承父类的元类,换而言之,类对象和元类对象有着同样的继承关系。
既然类是一个对象,那么元类是一个对象吗?其实元类也是一个对象,它是根元类(root metaclass)的实例。
那么,根元类呢?这么循环下去什么时候是个头。其实根元类所属的类也是根元类,即它本身。根元类指的就是根类的元类,也就是根类 NSObject
的元类。