Objective-C 对象内存模型

Objective-C 是一门面向对象的程序设计语言,它的对象模型是基于类来建立的。我们可以在这里下载最新的runtime源码,当前最新版本为objc4-709.tar.gz

Objective-C中的对象

NSObject是Objective-C中的基类,NSObject定义在objc4-709/runtime/NSObject.h中:

@interface NSObject <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 指针就是这个结构体的第一个成员变量,而类的其它成员变量依次排列在结构体中。排列顺序如下图所示:


class-member

对象的实例方法调用时,通过对象的 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 指针。

对应关系的图如下图,下图很好的描述了对象,类,元类之间的关系:
(图片源自 这里

class-diagram

我们再来看看继承关系,由于类方法的定义是保存在元类 (metaclass) 中,而方法调用的规则是,如果该类没有一个方法的实现,则向它的父类继续查找。所以,为了保证父类的类方法可以在子类中可以被调用,所以子类的元类会继承父类的元类,换而言之,类对象和元类对象有着同样的继承关系。

既然类是一个对象,那么元类是一个对象吗?其实元类也是一个对象,它是根元类(root metaclass)的实例。

那么,根元类呢?这么循环下去什么时候是个头。其实根元类所属的类也是根元类,即它本身。根元类指的就是根类的元类,也就是根类 NSObject 的元类。

推荐阅读更多精彩内容