Objective-C 是一门基于类的对象系统,每一个对象都是某个类的实例;对象的 isa 指针指向它的类。该类描述对象的一些信息:数据占用的空间,变量的类型和布局等。该类也描述对象的一些行为:对象可以响应的选择器(selector)和对象实现的实例方法。

(注:面向对象的含义,类是一类事物的共有特性的描述,比如:“人”类中的“姓名”,“年龄”等属性,“吃饭”、“喝水”等行为,所有的人都具备,所以类是对象的属性和行为的描述)

类中的方法列表是实例方法(对象可以响应的选择子)的集合。当你向对象发送一个消息的时候,objc_msgSend() 会通过查询该对象的类(和父类)中的方法列表来决定调用什么方法。

每一个 Objective-C 的类也是一个对象。它也有自己的 isa 指针、数据以及它可以响应的选择子。当你调用一个类方法的时候,例如 [NSObject alloc],实际上你是向这个类对象(NSObject)发送了一条消息。

由于类也是对象,那么它一定也是某一个类的实例,即元类。就像类是普通对象的描述一样,元类是类对象的描述。值得一提的是,元类的方法列表中存的是类方法:类对象可以响应的选择子。当你向一个类对象(元类的实例)发送消息(调用类方法)的时候,objc_msgSend() 会通过查询该类对象的元类(和父元类)中的方法列表来决定调用什么方法。就像实例方法是类对对象的行为的描述一样,类方法是元类对类对象的行为的描述(有点拗口)。

那么元类呢?是最底层的吗?不,元类是根类(NSObject)的元类的实例,根元类是根元类自己的实例,isa 指针链在一个循环处结束:对象 -> 类对象 -> 元类 -> 根元类 -> 根元类自身(-> 指向)。元类的 isa 指针的行为很少受到关注,因为在现实中没有人可以向元类对象发送消息。

那么元类的父类是谁呢?元类的父类链平行于类的父类链,所以类方法的继承关系和实例方法是一样的。并且根元类的父类是根类(即,NSObject 的元类的父类是它自己)(划重点),所以每一个类对象都可以响应根类的实例方法(即,根类的实例方法其实也是每一个类的类方法)。所以,类对象也和其他对象一样,都是根类的实例,但是类对象也是根类的子类。

是不是很迷惑,上面这张图可以帮助到你!请记住,当你向一个对象发消息的时候,方法的查找会从对象的 isa 指针开始,然后继续顺着父类链查找。“实例方法”在类中定义,“类方法”在元类和根类中定义。

(注:方法列表中并没有什么值来规定某一个方法是类方法还是实例方法,一个对象能通过 isa 指针和父类链找到的那个方法就是它能响应的方法,所以,类对象可以通过根元类的父类指针找到根类中的方法,那么它就可以响应这个方法,即使你认为根类中存的应该是“实例方法”。)

在正式的计算机科学语言中,类和元类的继承制度是更自由的形式,比如,更深层的元类链和很多类是某单一元类的实例。OC相对于 使用元类实现像类方法这样的实际目标,更趋向于隐藏元类。例如: 其实 [NSObject class][NSObejct self] 是等价的,即使,理论上它应该返回 NSObject -> isa 指向的的元类。

本文参考苹果公司 runtime 源码的维护者 Greg Parker 的 blog: [objc explain]: Classes and metaclasses