(原文出处:Objective-C Internals | Always Processing)
简要介绍Objective-C运行时源代码,重点是目标和类类型的界说、承继的完成方法、根类的特殊情况,以及与元类查找相关的内容。
前一篇文章评论了Objective-C的类架构,并为类层次结构制作了一个目标图。在这里,咱们将在这些概念的基础上,经过查看类目标图的完成(类、超类和元类)来持续评论。
让咱们从一些关键类型的公共界说开端。在Objective-C中,Class类型代表任何类类型,id类型代表任何类的实例。Objective-C运行时头文件objc.h界说了这些类型:
/// 一个不透明类型表示了一个 Objective-C 类。
typedef struct objc_class *Class;
/// 类的实例。
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
/// 指向类实例的指针。
typedef struct objc_object *id;
如前一篇文章所述,Objective-C类也是目标,但在公共类型界说中并没有这种关系。但是,假如咱们查看内部类型界说,咱们的确能够看到这种关系。
首先,objc-private.h包含了实际的objc_object界说。尽管内部界说有许多C++的非虚函数,但它的仅有成员变量isa_storage对应于(已弃用的)isa实例变量。(我不知道为什么内部类型是字符数组,猜想这可能是为了防止在字段的各种重载中意外直接运用。我会在本文中更详细地评论isa字段。)
struct objc_object {
char isa_storage[sizeof(isa_t)];
};
接下来,objc-runtime-new.h包含了objc_class数据结构的界说。它有几个自己的成员变量,与objc_object一样,它也有许多C++的非虚函数。
struct objc_class : objc_object {
// Class ISA;
Class superclass;
cache_t cache; // 以前是缓存指针和虚函数表
class_data_bits_t bits; // class_rw_t * 加上自界说的rr/alloc标志
};
没有特定于元类的类型;元类仅仅一个类,尽管objc_class类型具有用于辨认和操作元类的内部支撑。
在这里,咱们看到objc_class派生自objc_object,因而承继了isa字段。因而,类目标的完成与任何其他目标类型相同。接下来是superclass字段,它指向父类目标,假如有的话。(cache和bits不是本文重点,咱们之后再评论。)
这便是构建Objective-C类图所需的全部内容:两个数据结构(objc_object和objc_class)和两个字段(isa和superclass)!
objc_class成员函数
接下来,咱们看一看objc_class中的成员函数,以了解架构图中边的完成。
// 根类
bool isRootClass() {
return getSuperclass() == nil;
}
假如一个类没有超类,那么它是一个根类。但在实践中,这是不常见的,因为几乎所有的Objective-C目标都是从NSObject(或在稀有情况下是NSProxy)派生的。因而,根元类不是根类。
// 根元类
bool isRootMetaclass() {
return ISA() == (Class)this;
}
根元类具有自指的isa指针,这是运行时标识根元类的方法。据我所知,这是类图中仅有的循环。
// 元类标识
bool isMetaClass() const {
return cache.getBit(FAST_CACHE_META);
}
// 与isMetaClass相同,但也适用于未完成的类
bool isMetaClassMaybeUnrealized() {
if (isStubClass())
return false;
return bits.flags() & RW_META;
}
由编译器发出的一个位标志标识一个元类实例,这是将元类实例与类实例区分隔的主要特征。
未完成的类,包括存根类,在Objective-C内部探秘4: 未完成的类(和桥接)
一文中有更详细的描绘。
// 获取元类
// 当这是元类时,与this->ISA不同
Class getMeta() {
if (isMetaClassMaybeUnrealized()) return (Class)this;
else return this->ISA();
}
从某个类实例检索元类时,有必要查看该实例是否为元类。假如是元类,则回来它自己。不然,类实例经过其isa指针回来元类。
编译器输出
objc_class数据结构是Objective-C ABI的一部分,这意味着其巨细和字段布局的详细信息已知于第三方程序,它们将这些信息编码到其可执行二进制文件中。经过运行clang -S MyObject.m为上述MyObject.m文件生成汇编文件,将会生成包含以下片段(和更多内容)的汇编文件。
.section __DATA,__objc_data
_OBJC_CLASS_$_MyObject:
.quad _OBJC_METACLASS_$_MyObject
.quad _OBJC_CLASS_$_NSObject
.quad __objc_empty_cache
.quad 0
.quad __OBJC_CLASS_RO_$_MyObject
_OBJC_METACLASS_$_MyObject:
.quad _OBJC_METACLASS_$_NSObject
.quad _OBJC_METACLASS_$_NSObject
.quad __objc_empty_cache
.quad 0
.quad __OBJC_METACLASS_RO_$_MyObject
在这里,咱们能够看到编译器生成的代码与咱们从前一篇文章的架构图中得出的观察一致:
MyClass类目标有:
- 一个isa变量,指向MyClass元类。
- 一个super变量,指向NSObject类目标。
MyClass元类有:
- 一个isa变量,指向NSObject(根目标)元类。
- 一个super变量,指向NSObject元类。
(如上所述,cache和bits字段将在未来的文章中进行评论。)