(原文出处:Objective-C Internals | Always Processing)
Objective-C有一个独特的类结构,其中类是对象。它的优雅设计允许通过使用编译器生成的元类对所有对象类型进行动态方法派发。
Objective-C,像许多流行的编程语言一样,采用基于类的面向对象编程风格。为了讨论Objective-C的类架构,我们将重点放在类的行为(方法和属性)上,暂时不涉及类的状态(即实例变量,在本文中不涉及)。
尽管属性提供了对类状态的访问,但在Objective-C中它们只是方法的语法糖。(这个话题可以在之后的文章里探讨🤣)
以下六行代码足以探索Objective-C的类架构:
scss
@interface MyObject: NSObject
+ (void)classMethod;o
- (void)instanceMethod;
@end
MyObject *object = [[MyObject alloc] init];
方法派发
上述最后一行实例化了类MyObject,并将新的对象实例赋值给变量object。新的对象实例具有自己的状态,并且可以响应消息(即方法调用),例如-instanceMethod和-init。
Objective-C对每个消息发送(即每个方法调用)使用动态派发[1]。运行时通过在实例的isa变量所引用的类对象中查找选择器(即方法名)来找到方法实现。(所有Objective-C对象中的第一个实例变量是isa指针,它由编译器自动插入并由运行时初始化。)
前面段落中使用类对象这个术语是有意义的:在Objective-C中,类也是对象!这个巧妙的设计是类方法语言特性的基础(例如调用[NSObject alloc]或[MyObject classMethod]):它使得类方法可以完全多态(即子类可以重写类方法),并且消除了类方法和实例方法之间的运行时区别(类方法和实例方法都通过objc_msgSend进行派发)。
因为类也是对象,它们也有一个isa指针,指向元类。元类为类对象提供了选择器到类方法实现的映射,就像类对象为类实例(即对象)提供选择器到实例方法实现的映射一样。
继承
任何类或元类只为类实现的方法提供选择器到方法实现的映射。在上面的代码示例中,MyObject的类对象具有instanceMethod的映射,而MyObject的元类具有classMethod的映射。
MyObject还响应于+alloc和-init,这些方法在NSObject中实现。每个类对象(包括每个元类)都有一个指向其超类的引用。在解析选择器时,如果类/元类对象没有定义映射,运行时会在下一个超类中搜索定义。(尽管运行时提供了对异常的处理方法,但如果在超类链的末尾找不到定义,运行时仍将抛出异常。)
架构图
下面的图示说明了上面讨论的Objective-C类的设计:

对象实例有一个isa变量,指向MyClass类对象。
MyClass类对象有:
- 一个isa变量,指向MyClass元类。
- 一个super变量,指向NSObject类对象。
MyClass元类有:
- 一个isa变量,指向NSObject(根对象)元类。
- 一个super变量,指向NSObject元类。
图中还显示了一些上述讨论未涵盖的细节:
- 每个元类的isa变量都指向根对象的元类,包括根对象元类本身。isa变量不为nil是有意义的(对象必须有某种类型),但我不确定为什么所有元类的类型都是根元类的类型,而不是运行时提供的类型。不过,实际上这并不重要,因为元类本身永远不会接收消息。
- 根元类的超类是根类对象。当我在研究图示时,这一点让我感到惊讶。我一开始并没有明确地理解为什么会存在这个链接,我认为它的超类会是nil。