[翻译]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字段将在未来的文章中进行探讨。)

相关推荐
游戏开发爱好者840 分钟前
iOS 抓包工具实战 开发者的工具矩阵与真机排查流程
android·ios·小程序·https·uni-app·iphone·webview
Bigger13 小时前
🚀 Flutter iOS App 上架 App Store 全流程(图文详解)
flutter·ios·app
2501_9159090614 小时前
网络调试工具推荐 Fiddler抓包工具使用教程与代理设置详解(HTTP/HTTPS配置与实战技巧)
网络·http·ios·小程序·fiddler·uni-app·webview
我唔知啊20 小时前
SwiftUI 无限循环轮播图 支持手动控制
ios·swiftui
QuantumLeap丶1 天前
《Flutter全栈开发实战指南:从零到高级》- 08 -导航与路由管理
flutter·ios·dart
LinkTime_Cloud1 天前
苹果牵手SpaceX,iPhone 18 Pro将实现卫星直接上网
ios·iphone
2501_915921431 天前
iOS 26 描述文件管理与开发环境配置 多工具协作的实战指南
android·macos·ios·小程序·uni-app·cocoa·iphone
2501_915909061 天前
iOS 抓包实战 从原理到复现、定位与真机取证全流程
android·ios·小程序·https·uni-app·iphone·webview
2501_915106321 天前
HBuilder 上架 iOS 应用全流程指南:从云打包到开心上架(Appuploader)上传的跨平台发布实践
android·ios·小程序·https·uni-app·iphone·webview
2501_938782091 天前
从实例到单例:Objective-C 单例类的线程安全实现方案
开发语言·macos·objective-c