objc_class 结构体逐行解析

前言

objc_class 开始,是因为它是整个 Runtime 的基础数据结构。Runtime 管的事很多------消息发送、方法查找、内存管理、Category 加载......但这些行为最终都要落在"类长什么样"上面。搞清楚 objc_class,后面的东西才能接得上。

一、源码全貌(先看完整结构)

下面是从 Apple 开源的 objc4 里提取的核心结构体,我做了适度精简,保留所有关键字段。

建议先整体扫一遍,有个印象,后面逐个解释。

arduino 复制代码
// ============================================================
// 文件:objc-runtime-new.h(objc4-818.2)
// 源码地址:https://opensource.apple.com/source/objc4/
// ============================================================

// -------------------- 1. objc_object --------------------
// 所有 OC 对象的基类,只有一个字段:isa
struct objc_object {
private:
    isa_t isa;  // 64位,包含类指针+引用计数+标志位

public:
    Class ISA(bool authenticated = false);
    Class getIsa();
    // ... 省略其他方法
};


// -------------------- 2. objc_class --------------------
// 这就是"类"的底层结构,继承自 objc_object
struct objc_class : objc_object {
    // 注意:isa 字段继承自 objc_object,这里不重复写
    
    Class superclass;           // 父类指针
    cache_t cache;              // 方法缓存(哈希表)
    class_data_bits_t bits;     // 指向 class_rw_t 的指针+标志位

    // 取出真正的数据
    class_rw_t *data() const {
        return bits.data();
    }
    // ... 省略其他方法
};


// -------------------- 3. class_data_bits_t --------------------
// 这是 objc_class.bits 的类型,用来存储指向 class_rw_t 的指针 + 几个标志位
struct class_data_bits_t {
private:
    uintptr_t bits;   // 就是一个 64 位整数,低位藏标志位,高位存指针

public:
    // 用掩码取出真正的 class_rw_t 指针
    class_rw_t *data() const {
        return (class_rw_t *)(bits & FAST_DATA_MASK);
    }

    // 各种标志位的读取方法
    bool isSwiftLegacy() const {
        return getBit(FAST_IS_SWIFT_LEGACY);
    }
    bool isSwiftStable() const {
        return getBit(FAST_IS_SWIFT_STABLE);
    }
    // ... 其他方法
};

// ARM64 下的掩码和标志位定义:
// FAST_DATA_MASK      = 0x00007ffffffffff8UL  (取 bit 3~46,即真正的指针)
// FAST_IS_SWIFT_LEGACY = 1 << 0  (bit 0: 是否是旧版 Swift 类)
// FAST_IS_SWIFT_STABLE = 1 << 1  (bit 1: 是否是新版 Swift 类)
// FAST_HAS_DEFAULT_RR  = 1 << 2  (bit 2: 是否有默认的 retain/release)


// -------------------- 4. cache_t --------------------
// 方法缓存,加速方法查找
struct cache_t {
private:
    explicit_atomic<uintptr_t> _bucketsAndMaybeMask;  // 桶数组地址
    union {
        struct {
            explicit_atomic<mask_t>    _maybeMask;   // 桶数量-1(用于哈希取模)
            uint16_t                   _flags;
            uint16_t                   _occupied;    // 已使用的桶数
        };
        explicit_atomic<preopt_cache_t *> _originalPreoptCache;
    };
public:
    // ... 省略查找、插入方法
};

// 单个缓存桶
struct bucket_t {
private:
    explicit_atomic<SEL> _sel;       // 方法名(选择子)
    explicit_atomic<uintptr_t> _imp; // 函数指针(方法实现地址)
};


// -------------------- 5. class_rw_t --------------------
// 运行时可读写数据(Category 方法会合并到这里)
struct class_rw_t {
    uint32_t flags;
    uint16_t witness;
    uint16_t index;

    explicit_atomic<uintptr_t> ro_or_rw_ext;  // 指向 class_ro_t 或扩展数据

    Class firstSubclass;       // 第一个子类
    Class nextSiblingClass;    // 兄弟类(形成链表)

    // 获取方法/属性/协议列表
    const method_array_t methods() const;
    const property_array_t properties() const;
    const protocol_array_t protocols() const;

    // 获取只读数据
    const class_ro_t *ro() const;
};


// -------------------- 6. class_ro_t --------------------
// 编译期只读数据(源码里写死的方法、变量、属性)
struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;      // 实例变量起始偏移
    uint32_t instanceSize;       // sizeof(实例),对象占多少字节

    const uint8_t * ivarLayout;  // 强引用 ivar 的内存布局

    const char * name;           // 类名字符串,如 "NSString"
    
    WrappedPtr<method_list_t, method_list_t::Ptrauth> baseMethods;  // 方法列表
    protocol_list_t * baseProtocols;     // 协议列表
    const ivar_list_t * ivars;           // 实例变量列表
    
    const uint8_t * weakIvarLayout;      // 弱引用 ivar 的内存布局
    property_list_t *baseProperties;     // 属性列表
};


// -------------------- 7. method_t --------------------
// 单个方法的描述
struct method_t {
    SEL name;              // 方法名(选择子),本质是 const char *
    const char *types;     // 类型编码,如 "v16@0:8"
    IMP imp;               // 函数指针(真正的代码地址)
};


// -------------------- 8. ivar_t --------------------
// 单个实例变量的描述
struct ivar_t {
    int32_t *offset;       // 偏移量指针(Non-Fragile ABI 用)
    const char *name;      // 变量名,如 "_name"
    const char *type;      // 类型编码,如 "@"NSString""
    uint32_t alignment_raw;// 对齐方式
    uint32_t size;         // 占多少字节
};


// -------------------- 9. isa_t --------------------
// isa 的真正定义(union,64位里塞了很多信息)
union isa_t {
    uintptr_t bits;        // 原始64位值

    // ARM64 位域展开(iOS 真机):
    struct {
        uintptr_t nonpointer        : 1;   // bit 0:  是否是优化过的 isa
        uintptr_t has_assoc         : 1;   // bit 1:  有关联对象?
        uintptr_t has_cxx_dtor      : 1;   // bit 2:  有 C++ 析构?
        uintptr_t shiftcls          : 33;  // bit 3-35:  类指针(右移3位存储)
        uintptr_t magic             : 6;   // bit 36-41: 固定值 0x1a,调试用
        uintptr_t weakly_referenced : 1;   // bit 42: 被弱引用?
        uintptr_t unused            : 1;   // bit 43: 未使用
        uintptr_t has_sidetable_rc  : 1;   // bit 44: 引用计数溢出到 SideTable?
        uintptr_t extra_rc          : 19;  // bit 45-63: 引用计数-1
    };
};

二、结构关系图

scss 复制代码
objc_class(一个类在内存里的样子)
┌─────────────────────────────────────┐
│  isa (继承自 objc_object)           │ ← isa_t union,64位
├─────────────────────────────────────┤
│  superclass                         │ ← 指向父类的 objc_class
├─────────────────────────────────────┤
│  cache                              │ ← cache_t 结构体
│    └── bucket_t[] 数组              │     每个桶存 { SEL, IMP }
├─────────────────────────────────────┤
│  bits                               │ ← class_data_bits_t(指针+标志位)
│    └── data() ───────────────────────────→ class_rw_t(运行时可写)
│                                     │        ├── methods()
│                                     │        ├── properties()
│                                     │        ├── protocols()
│                                     │        └── ro() ────────→ class_ro_t(只读)
│                                     │                             ├── name
│                                     │                             ├── baseMethods
│                                     │                             │     └── method_t[]
│                                     │                             ├── ivars
│                                     │                             │     └── ivar_t[]
│                                     │                             └── baseProperties
└─────────────────────────────────────┘

三、逐结构体解析

接下来按源码出现的顺序,逐个讲解每个结构体、每个字段的含义。


3.1 objc_object ------ 所有对象的祖宗

arduino 复制代码
struct objc_object {
private:
    isa_t isa;
};

这是什么?

这是 OC 里所有对象 的底层表示。不管是 NSStringUIView、还是你自定义的 MyClass 实例,底层都是 objc_object

字段解析

字段 类型 含义
isa isa_t "is a" 的缩写,标识"这个对象是什么类型"。是一个 64 位的 union,里面藏了类指针 + 引用计数 + 各种标志位。isa_t 的详细结构会在第二篇展开讲解。

为什么只有一个字段?

因为 objc_object最小公共祖先。每个对象只需要知道"我是什么类型"(isa),其他的成员变量由具体的类定义,紧跟在 isa 后面存储。

内存布局示意

vbnet 复制代码
一个 MyClass 实例的内存:
┌────────────────┐ ← 对象起始地址
│     isa        │   8 字节(objc_object 的字段)
├────────────────┤
│    _name       │   8 字节(MyClass 自己的 ivar)
├────────────────┤
│    _age        │   4 字节(MyClass 自己的 ivar)
└────────────────┘

3.2 objc_class ------ 类的完整定义

arduino 复制代码
struct objc_class : objc_object {
    Class superclass;
    cache_t cache;
    class_data_bits_t bits;

    class_rw_t *data() const {
        return bits.data();
    }
};

这是什么?

这是 OC 里 的底层表示。每个 @interface MyClass 在运行时都对应一个 objc_class 结构体实例。

注意它继承自 objc_object,所以"类也是对象"------类对象有自己的 isa(指向元类)。

字段逐个解析

字段 类型 含义
isa isa_t(继承来的) 类对象的 isa 指向它的元类 (metaclass)。isa_t 的详细结构见第二篇。
superclass Class 父类指针。Classobjc_class * 的 typedef,即指向另一个 objc_class 的指针。NSObject 的 superclass 是 nil。;
cache cache_t 方法缓存,哈希表结构。最近调用的方法会缓存在这里,加速后续调用。
bits class_data_bits_t 一个 64 位整数,低 3 位是标志位,高位是 class_rw_t 指针

Class 是什么类型?

arduino 复制代码
// objc.h
typedef struct objc_class *Class;

Class 就是 objc_class * 的别名,一个指向类对象的指针。你代码里写的所有 Class 都只是这个指针,没有额外结构:

ini 复制代码
Class cls = [MyClass class];       // 拿到 MyClass 的 objc_class * 指针
Class superCls = [cls superclass]; // 拿到父类的 objc_class * 指针

同理,id 也是:

arduino 复制代码
typedef struct objc_object *id;    // id = objc_object *,指向任意实例对象

superclass 有什么用?

实现继承 。当在当前类找不到方法时,runtime 会沿着 superclass 链往上找。

css 复制代码
调用 [myObj doSomething]
    ↓
在 MyClass 的方法列表里找
    ↓ 找不到
通过 superclass 到 NSObject 里找
    ↓ 还找不到
触发消息转发

3.3 class_data_bits_t ------ 指针 + 标志位的混合体

arduino 复制代码
struct class_data_bits_t {
private:
    uintptr_t bits;   // 64 位整数

public:
    class_rw_t *data() const {
        return (class_rw_t *)(bits & FAST_DATA_MASK);
    }
};

这是什么?

就是 objc_class.bits 的类型。它不是简单的指针,而是把 class_rw_t 指针几个标志位 打包进同一个 64 位整数里。

为什么能这样做?

因为 class_rw_t 在内存里是 8 字节对齐 的,所以它的地址的低 3 位永远是 000。苹果就把这 3 位拿来存标志位,不浪费。

64 位的布局

arduino 复制代码
class_data_bits_t.bits(64位)

 63                                3  2  1  0
┌────────────────────────────────┬──┬──┬──┐
│     class_rw_t 指针 (bit 3~63) │ 2│ 1│ 0│
└────────────────────────────────┴──┴──┴──┘
                                   │  │  │
                                   │  │  └─ FAST_IS_SWIFT_LEGACY (是旧版Swift类?)
                                   │  └──── FAST_IS_SWIFT_STABLE (是新版Swift类?)
                                   └─────── FAST_HAS_DEFAULT_RR  (有默认retain/release?)

取指针的掩码

arduino 复制代码
// ARM64
#define FAST_DATA_MASK 0x00007ffffffffff8UL

// 二进制:...11111111111111111111111111111111111111000
// 作用:与运算后,低 3 位清零,剩下的就是真正的 class_rw_t 地址

data() 方法做了什么?

arduino 复制代码
class_rw_t *data() const {
    return (class_rw_t *)(bits & FAST_DATA_MASK);
    // bits & 掩码 → 把低 3 位标志位清掉 → 得到纯净的 class_rw_t 指针
}

一句话总结

class_data_bits_tisa_t 的设计思路一样------充分利用内存对齐带来的空闲位,一个 64 位整数里塞多种信息,省内存


3.4 cache_t ------ 方法缓存

arduino 复制代码
struct cache_t {
private:
    explicit_atomic<uintptr_t> _bucketsAndMaybeMask;
    union {
        struct {
            explicit_atomic<mask_t> _maybeMask;
            uint16_t _flags;
            uint16_t _occupied;
        };
        explicit_atomic<preopt_cache_t *> _originalPreoptCache;
    };
};

struct bucket_t {
private:
    explicit_atomic<SEL> _sel;
    explicit_atomic<uintptr_t> _imp;
};

为什么需要缓存?

每次调用方法都去 class_rw_t 的方法列表里遍历查找,太慢了。cache_t 是一个哈希表,把最近调用过的方法缓存起来。

字段解析

字段 含义
_bucketsAndMaybeMask 哈希桶数组的起始地址
_maybeMask 桶数量 - 1,用于哈希取模(hash & mask
_occupied 当前已使用的桶数量

bucket_t 是什么?

单个缓存条目,存储 SEL(方法名)和 IMP(函数指针)的映射。

字段 类型 含义
_sel SEL 方法选择子(方法名),如 @selector(viewDidLoad)
_imp uintptr_t 方法实现的函数地址

查找流程

java 复制代码
[obj doSomething]
    ↓
计算 @selector(doSomething) 的哈希值
    ↓
hash & _maybeMask → 得到桶的索引
    ↓
取出 bucket_t,比较 _sel 是否等于 @selector(doSomething)
    ↓
相等 → 直接调用 _imp,结束(命中缓存,极快)
不相等 → 去 class_rw_t 里慢速查找

缓存什么时候会失效?

  • 调用 method_exchangeImplementations(Method Swizzle)后
  • 动态添加方法后
  • 类第一次加载时

失效时 runtime 会调用 flushCaches() 清空缓存。


3.5 class_rw_t ------ 运行时可读写数据

arduino 复制代码
struct class_rw_t {
    uint32_t flags;
    uint16_t witness;
    uint16_t index;

    explicit_atomic<uintptr_t> ro_or_rw_ext;

    Class firstSubclass;
    Class nextSiblingClass;

    const method_array_t methods() const;
    const property_array_t properties() const;
    const protocol_array_t protocols() const;
    const class_ro_t *ro() const;
};

这是什么?

rw = read-write (可读写)。这里存放运行时可以修改的数据,比如 Category 添加的方法会合并到这里。

字段解析

字段 含义
flags 各种标志位(是否已初始化、是否有 C++ 构造函数等)
ro_or_rw_ext 指向 class_ro_t(只读数据),或扩展数据
firstSubclass 指向第一个子类,形成子类链表
nextSiblingClass 指向下一个兄弟类(同一个父类的其他子类)

获取方法/属性/协议

arduino 复制代码
const method_array_t methods() const;     // 返回方法列表(含 Category 方法)
const property_array_t properties() const; // 返回属性列表
const protocol_array_t protocols() const;  // 返回协议列表

这些方法返回的是合并后的列表------源码里写的 + Category 加进来的。

ro() 方法

返回 class_ro_t 指针,取出编译期确定的只读数据。


3.6 class_ro_t ------ 编译期只读数据

arduino 复制代码
struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;

    const uint8_t * ivarLayout;
    const char * name;
    
    WrappedPtr<method_list_t, ...> baseMethods;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;
    
    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;
};

这是什么?

ro = read-only (只读)。这里存放编译时就确定的数据,运行时不能修改。

字段逐个解析

字段 类型 含义
flags uint32_t 标志位
instanceStart uint32_t 实例变量在对象内存中的起始偏移(通常是 8,跳过 isa)
instanceSize uint32_t 一个实例对象占多少字节(sizeof
ivarLayout const uint8_t * 描述哪些 ivar 是强引用(ARC 用)
name const char * 类名字符串,如 "UIViewController"
baseMethods method_list_t * 源码里定义的方法列表(不含 Category)
baseProtocols protocol_list_t * 源码里遵循的协议列表
ivars ivar_list_t * 实例变量列表
weakIvarLayout const uint8_t * 描述哪些 ivar 是弱引用
baseProperties property_list_t * 源码里定义的属性列表

class_ro_t vs class_rw_t 对比

class_ro_t class_rw_t
全称 read-only read-write
什么时候确定 编译期(写进 Mach-O 二进制文件) 运行时(启动时构造)
能修改吗 ❌ 不能 ✅ 能
存什么 源码里写死的方法、属性、变量 动态添加的方法、Category 合并的方法

为什么要分两层?

因为 Category 是运行时加载的。编译期不知道会有哪些 Category,所以:

  1. 编译期:把源码里写的方法存进 class_ro_t
  2. 运行时:遍历所有 Category,把它们的方法合并class_rw_t

查找方法时,先查 class_rw_t(含 Category),它内部会访问 class_ro_t


3.7 method_t ------ 单个方法

ini 复制代码
struct method_t {
    SEL name;
    const char *types;
    IMP imp;
};

字段解析

字段 类型 含义 例子
name SEL 方法选择子(方法名) @selector(viewDidLoad)
types const char * 类型编码(返回值+参数的类型) "v16@0:8"
imp IMP 函数指针,指向方法的真正实现 0x100001234(代码段地址)

SEL 是什么?

arduino 复制代码
typedef struct objc_selector *SEL;

本质是一个唯一化的 C 字符串。同名方法在整个程序里 SEL 值相同(指针相等),所以比较方法名只需要比较指针,极快。

ini 复制代码
SEL sel1 = @selector(doSomething);
SEL sel2 = @selector(doSomething);
// sel1 == sel2(指针相等,不是字符串比较)

IMP 是什么?

arduino 复制代码
typedef void (*IMP)(id, SEL, ...);

函数指针,前两个参数固定是:

  • id self:消息接收者
  • SEL _cmd:方法选择子

这解释了为什么 OC 方法里能直接用 self_cmd------它们是函数的隐藏参数。

objectivec 复制代码
// 你写的:
- (void)doSomething {
    NSLog(@"%@", self);
}

// 编译器眼里的:
void doSomething(id self, SEL _cmd) {
    NSLog(@"%@", self);
}

types 字符串怎么读?

- (NSString *)nameWithPrefix:(NSString *)prefix 为例,types 是 @24@0:8@16

bash 复制代码
@    → 返回值是 id(对象)
24   → 所有参数总共占 24 字节
@    → 第1个参数是 id(self)
0    → 从第 0 字节开始
:    → 第2个参数是 SEL(_cmd)
8    → 从第 8 字节开始
@    → 第3个参数是 id(prefix)
16   → 从第 16 字节开始

这套编码叫 Type Encoding,runtime 靠它做方法签名校验。


3.8 ivar_t ------ 单个实例变量

arduino 复制代码
struct ivar_t {
    int32_t *offset;
    const char *name;
    const char *type;
    uint32_t alignment_raw;
    uint32_t size;
};

字段解析

字段 类型 含义 例子
offset int32_t * 偏移量的指针(不是值!) 指向存储偏移量的内存
name const char * 变量名 "_name"
type const char * 类型编码 "@"NSString""
alignment_raw uint32_t 内存对齐方式 通常是 3(2^3 = 8 字节对齐)
size uint32_t 占多少字节 指针占 8 字节

为什么 offset 是指针而不是值?

这是 Non-Fragile ABI(非脆弱 ABI)的设计。

假设父类 NSObject 有 8 字节的 isa,子类 MyClass_name 变量在 offset 8。

如果 Apple 在新系统里给 NSObject 加了一个成员变量(变成 16 字节),按老 ABI,MyClass_name 还在 offset 8,就会和 NSObject 新增的变量重叠------程序崩溃。

Non-Fragile ABI 的解决方案:

  1. offset 是指针,不是值
  2. App 启动时,runtime 检查父类大小是否变化
  3. 如果变化了,自动调整所有子类 ivar 的 offset 值
  4. 子类不需要重新编译
objectivec 复制代码
旧系统:NSObject 8字节,MyClass._name 在 offset 8
    ↓ Apple 升级系统
新系统:NSObject 16字节
    ↓ runtime 自动修正
MyClass._name 的 offset 从 8 改成 16

访问 ivar 的过程

java 复制代码
// 伪代码
id value = *(id *)((char *)obj + *ivar->offset);
// 1. 取出 offset 指针指向的偏移值
// 2. 对象地址 + 偏移值 = ivar 的内存地址
// 3. 解引用得到 ivar 的值

3.9 isa_t ------ 64 位里藏了很多东西

arduino 复制代码
union isa_t {
    uintptr_t bits;

    struct {
        uintptr_t nonpointer        : 1;
        uintptr_t has_assoc         : 1;
        uintptr_t has_cxx_dtor      : 1;
        uintptr_t shiftcls          : 33;
        uintptr_t magic             : 6;
        uintptr_t weakly_referenced : 1;
        uintptr_t unused            : 1;
        uintptr_t has_sidetable_rc  : 1;
        uintptr_t extra_rc          : 19;
    };
};

为什么不直接存指针?

在 64 位系统上,指针只需要约 40 位就能表示所有内存地址。剩下的位"浪费"了,苹果就把引用计数和各种标志位塞进去,省内存。

这叫 Non-pointer ISA(优化过的 isa)。

每一位的含义

位域 位数 含义
nonpointer 1 是否是优化过的 isa(现代设备都是 1)
has_assoc 1 对象是否有关联对象(objc_setAssociatedObject
has_cxx_dtor 1 是否有 C++ 析构函数或 ARC 的 .cxx_destruct
shiftcls 33 类指针(右移 3 位存储,取时左移还原)
magic 6 固定值 0x1a,调试用(值不对说明内存被踩了)
weakly_referenced 1 是否被 __weak 指针指向过
unused 1 未使用,预留
has_sidetable_rc 1 引用计数是否溢出到 SideTable
extra_rc 19 存储引用计数 - 1(最大 2^19 - 1 = 524287)

如何取出类指针?

ini 复制代码
Class cls = (Class)(isa.bits & ISA_MASK);
// ISA_MASK = 0x0000000ffffffff8ULL
// 掩码取出 bit 3~35,然后隐含左移还原

四、完整内存布局示意

把所有结构体串起来,一个类在内存里长这样:

scss 复制代码
objc_class 实例(代表 MyClass 这个类)
┌─────────────────────────────────────────────────────┐
│  isa (64位 isa_t)                                   │ → 指向 Meta-MyClass(元类)
├─────────────────────────────────────────────────────┤
│  superclass (8字节)                                 │ → 指向 NSObject
├─────────────────────────────────────────────────────┤
│  cache (cache_t)                                    │
│    _bucketsAndMaybeMask → [ bucket_t, bucket_t... ] │ 每个桶: { SEL, IMP }
│    _maybeMask = N-1                                 │
│    _occupied = 已用桶数                              │
├─────────────────────────────────────────────────────┤
│  bits (class_data_bits_t)                           │ ← 低3位是标志位,高位是指针
│    data() ──────────────────────────────────────────│──→ class_rw_t
│                                                     │      ├── methods()   → [method_t, ...]
│                                                     │      ├── properties()→ [property_t, ...]
│                                                     │      ├── protocols() → [protocol_t, ...]
│                                                     │      └── ro() ───────→ class_ro_t
│                                                     │              ├── name = "MyClass"
│                                                     │              ├── instanceSize = 24
│                                                     │              ├── baseMethods
│                                                     │              │     ├── method_t { SEL, types, IMP }
│                                                     │              │     └── method_t { ... }
│                                                     │              └── ivars
│                                                     │                    ├── ivar_t { offset*, "_name", "@", 3, 8 }
│                                                     │                    └── ivar_t { offset*, "_age",  "i", 2, 4 }
└─────────────────────────────────────────────────────┘

五、小结

结构体 可否运行时修改 存放什么
objc_class 不直接改 类的容器,持有 superclass/cache/bits
isa_t 部分可改(引用计数位) 类指针 + 引用计数 + 标志位,全塞在 64 位里
class_data_bits_t 不直接改 class_rw_t 指针 + 3 个标志位,又一个"指针+标志"混合体
cache_t 是(每次调用方法后更新) 最近调用的方法 SEL → IMP 映射
class_rw_t 运行时合并后的方法、属性、协议
class_ro_t 编译期确定的方法、变量、属性,写死在二进制里
method_t IMP 可以换(Swizzle) 一个方法的名字、类型编码、实现地址
ivar_t offset 可改(Non-Fragile ABI) 一个实例变量的名字、类型、偏移量

下一篇:isa 指针深度解析、元类体系、完整继承链图

相关推荐
2501_915918413 小时前
网站抓包解析,掌握浏览器请求和 HTTPS 数据分析的流程
android·ios·小程序·https·uni-app·iphone·webview
00后程序员张4 小时前
iOS开发者工具有哪些?Xcode、AppUploader(开心上架)、Fastlane如何使用
android·macos·ios·小程序·uni-app·iphone·xcode
tangweiguo030519874 小时前
iOS多线程编程从入门到实战:一份通俗易懂的Objective-C指南
ios
游戏开发爱好者84 小时前
新的 iOS 开发工具体验,在快蝎 IDE 里完成应用开发与真机调试
ide·vscode·ios·objective-c·个人开发·swift·敏捷流程
for_ever_love__4 小时前
Objective-C学习 NSArray 和 NSMutableArray 功能详解
学习·ios·objective-c
ITKEY_4 小时前
flutter打包ipad 并上传Appstore
flutter·ios·ipad
SY.ZHOU1 天前
大型工程跨全平台实践总结
flutter·ios·安卓·鸿蒙
denggun123451 天前
Sendable 协议-Swift 结构化并发的核心安全保障
ios·swift
奶糖的次元空间1 天前
iOS 学习笔记 - 创建第一个APP
ios