[翻译]Objective-C内部探秘2:类图实现

(原文出处:Objective-C Internals | Always Processing)

简要介绍Objective-C运行时源代码,重点是对象和类类型的定义、继承的实现方式、根类的特殊情况,以及与元类查找相关的内容。

前一篇文章探讨了Objective-C的类架构,并为类层次结构绘制了一个对象图。在这里,我们将在这些概念的基础上,通过检查类对象图的实现(类、超类和元类)来继续探讨。

让我们从一些关键类型的公共定义开始。在Objective-C中,Class类型代表任何类类型,id类型代表任何类的实例。Objective-C运行时头文件objc.h定义了这些类型:

arduino 复制代码
/// 一个不透明类型表示了一个 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字段。)

c 复制代码
struct objc_object {
    char isa_storage[sizeof(isa_t)];
};

接下来,objc-runtime-new.h包含了objc_class数据结构的定义。它有几个自己的成员变量,与objc_object一样,它也有许多C++的非虚函数。

arduino 复制代码
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中的成员函数,以了解架构图中边的实现。

csharp 复制代码
// 根类
bool isRootClass() {
    return getSuperclass() == nil;
}

如果一个类没有超类,那么它是一个根类。但在实践中,这是不常见的,因为几乎所有的Objective-C对象都是从NSObject(或在罕见情况下是NSProxy)派生的。因此,根元类不是根类。

csharp 复制代码
// 根元类
bool isRootMetaclass() {
    return ISA() == (Class)this;
}

根元类具有自指的isa指针,这是运行时标识根元类的方式。据我所知,这是类图中唯一的循环。

csharp 复制代码
// 元类标识
bool isMetaClass() const {
    return cache.getBit(FAST_CACHE_META);
}

// 与isMetaClass相同,但也适用于未实现的类
bool isMetaClassMaybeUnrealized() {
    if (isStubClass())
        return false;
    return bits.flags() & RW_META;
}

由编译器发出的一个位标志标识一个元类实例,这是将元类实例与类实例区分开的主要特征。

未实现的类,包括存根类,在Objective-C内部探秘4: 未实现的类(和桥接)一文中有更详细的描述。

kotlin 复制代码
// 获取元类
// 当这是元类时,与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文件生成汇编文件,将会生成包含以下片段(和更多内容)的汇编文件。

bash 复制代码
.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字段将在未来的文章中进行探讨。)

相关推荐
若水无华1 天前
fiddler 配置ios手机代理调试
ios·智能手机·fiddler
Aress"1 天前
【ios越狱包安装失败?uniapp导出ipa文件如何安装到苹果手机】苹果IOS直接安装IPA文件
ios·uni-app·ipa安装
Jouzzy2 天前
【iOS安全】Dopamine越狱 iPhone X iOS 16.6 (20G75) | 解决Jailbreak failed with error
安全·ios·iphone
瓜子三百克2 天前
采用sherpa-onnx 实现 ios语音唤起的调研
macos·ios·cocoa
左钦杨2 天前
IOS CSS3 right transformX 动画卡顿 回弹
前端·ios·css3
努力成为包租婆2 天前
SDK does not contain ‘libarclite‘ at the path
ios
安和昂2 天前
【iOS】Tagged Pointer
macos·ios·cocoa
I烟雨云渊T3 天前
iOS 阅后即焚功能的实现
macos·ios·cocoa
struggle20253 天前
适用于 iOS 的 开源Ultralytics YOLO:应用程序和 Swift 软件包,用于在您自己的 iOS 应用程序中运行 YOLO
yolo·ios·开源·app·swift
Unlimitedz3 天前
iOS视频编码详细步骤(视频编码器,基于 VideoToolbox,支持硬件编码 H264/H265)
ios·音视频