Skip to content

01 - Runtime 深度 ​

目录 ​

  1. ObjC Runtime 架构
  2. 消息发送机制
  3. 动态绑定与转发
  4. Method Swizzling 全栈
  5. 分类(Category)与扩展(Extension)原理
  6. 关联对象(Associated Objects)
  7. Protocol Witness Table
  8. Swift Runtime
  9. 面试题汇总

1. ObjC Runtime 架构 ​

1.1 Runtime 系统概览 ​

ObjC Runtime 系统架构(源码级):
┌──────────────────────────────────────────────────────────────────────┐
│                                                              │
│  1. ObjC Runtime 核心(libobjc.A.dylib)                    │
│  ┌─────────────────────────────────────────────────────┐     │
│  │  objc/runtime.h  — 公共 API                            │     │
│  │  objc/message.h    — 消息发送                           │     │
│  │  objc/objc.h      — 基础类型定义                        │     │
│  │  objc/encoding.h  — 类型编码                            │     │
│  └─────────────────────────────────────────────────────┘     │
│                                                              │
│  2. Runtime 核心组件                                           │
│  ┌────────────────────────────────────────────────────┐     │
│  │  Class Table        — 类缓存表                        │     │
│  │  Method Lists       — 方法列表(Method List)           │     │
│  │  Protocol Table     — 协议表(Witness Table)          │     │
│  │  Ivar List         — 实例变量列表                      │     │
│  │  Selector Table    — 选择器表                          │     │
│  │  Protocol Cache    — 协议缓存                         │     │
│  └──────────────────────────────────────────────────┘     │
│                                                              │
│  3. 数据结构                                                 │
│  ┌────────────────────────────────────────────────────┐     │
│  │  objc_object    — 对象基类(isa 指针)                 │     │
│  │  objc_class     — 类描述(meta-class 元类)           │     │
│  │  Method        — 方法结构体                           │     │
│  │  Ivar          — 实例变量结构体                        │     │
│  │  Protocol      — 协议描述                             │     │
│  │  SEL           — 选择器(字符串标识符)               │     │
│  └────────────────────────────────────────────────────┘     │
│                                                              │
│  4. 内存管理                                                 │
│  ┌────────────────────────────────────────────────────┐     │
│  │  retain/release    — 引用计数操作                     │     │
│  │  weak 处理         — __weak 变量自动置 nil            │     │
│  │  weak 通知         — Weak Reference Table             │     │
│  │  associative refs  — 关联对象链表                     │     │
│  └────────────────────────────────────────────────────┘     │
│                                                              │
│  Runtime 调用流程:                                          │
│  ┌───────────────────────────────────────────────────┐     │
│  │  1. obj.method()  — 源代码                         │     │
│  │  2. objc_msgSend(obj, @selector(method))  — 编译     │     │
│  │  3. class_getInstanceMethod(class, sel)  — 查找      │     │
│  │  4. IMP imp = method_getImplementation(method)       │     │
│  │  5. imp(obj, sel, ...)  — 执行                       │     │
│  └────────────────────────────────────────────────┘     │
└─────────────────────────────────────────────────────┘

运行时结构体内存布局:
┌──────────────────────────────────────────────────────┐
│  objc_object(所有对象的基类)                         │
│                                                       │
│  ┌────────────────────────────────────────────────┐   │
│  │  isa 指针(8 字节,指向 Class 对象)             │   │
│  └──────────────────────────────────────────────┘   │
│                                                       │
│  Class 内存布局:                                     │
│  ┌──────────────────────────────────────────────┐   │
│  │  isa          — 指向 meta-class(元类)         │   │
│  │  superclass  — 指向父类                         │   │
│  │  cache        — 方法缓存(IMP 缓存)           │   │
│  │  data (rw)  — 类数据(包含 ivar/方法/协议等)   │   │
│  └───────────────────────────────────────────────┘   │
│                                                       │
│  元类(Meta-class)内存布局:                           │
│  ┌──────────────────────────────────────────────┐   │
│  │  isa          — 指向 metaclass(meta-class 的类)│   │
│  │  superclass  — 指向父类的 meta-class           │   │
│  │  cache        — 类方法缓存                       │   │
│  │  data (rw)  — 类数据(存储类方法)              │   │
│  └──────────────────────────────────────────────┘   │
└───────────────────────────────────────────────────────┘

1.2 Class 的初始化过程(源码级) ​

ObjC 类加载流程(dyld 阶段):
┌───────────────────────────────────────────────────────────┐
│  1. dyld 加载 Mach-O 文件                                    │
│     └─→ 加载所有依赖的 framework                              │
│     └─→ 解析所有符号(symbols)                               │
│     └─→ 调用所有 +load 方法(类/分类)                        │
│                                                              │
│  2. _objc_init 初始化(进程启动时)                            │
│     └─→ 初始化类缓存表(class hash table)                    │
│     └─→ 初始化方法缓存(method caches)                       │
│     └─→ 初始化 protocol 表                                   │
│     └─→ 初始化 weak 引用表                                    │
│                                                              │
│  3. 类加载(class-dump 阶段)                                 │
│     └─→ 读取 MH_OBJECT 段的 __objc_classlist 段              │
│     └─→ 为每个类创建 Class 结构体                             │
│     └─→ 解析 Class 的 Ivar List                             │
│     └─→ 解析 Class 的 Method List                            │
│     └─→ 解析 Class 的 Protocol List                           │
│     └─→ 创建 metaclass                                      │
│                                                              │
│  4. 类注册                                                   │
│     └─→ 注册到 class hash table                              │
│     └─→ 设置 super class 链                                  │
│     └─→ 初始化 class hierarchy                               │
│                                                              │
│  5. 类初始化(+initialize 方法)                              │
│     └─→ 线程安全的懒加载初始化                                │
│     └─→ 每个类只初始化一次                                    │
└─────────────────────────────────────────────────────────┘

Class 加载时序:
ObjC Class 加载流程:
┌────────┐   ┌──────────────────┐   ┌───────────────────┐   ┌─────────────┐
│ dyld    │──▶│ _objc_init       │──▶│ class-dump       │──▶│ +initialize  │
│ 加载     │   │ 初始化            │   │ 类注册            │   │ 懒初始化      │
└────────┘   └──────────────────┘   └───────────────────┘   └─────────────┘
     │              │                   │                    │
     ▼              ▼                   ▼                    ▼
  Mach-O        类缓存表            Class 结构体         线程安全
  解析            hash               创建               +initialize

2. 消息发送机制 ​

2.1 消息发送全链路 ​

objc
/*
消息发送流程(源码级):

[obj doSomething];  // 源代码
↓
objc_msgSend(obj, @selector(doSomething));  // 编译后
↓
查找 IMP(方法实现):

┌───────────────────────────────────────────────────────┐
│ 第 1 步:查找 Class 的 method cache(缓存查找)         │
│                                                        │
│  IMP imp = class_getMethodImplementation(cls, SEL);    │
│  - 在 cache 中查找 SEL                               │
│  - 命中 → 直接返回 IMP                               │
│  - 未命中 → 进入第 2 步                              │
│                                                        │
│  cache 结构:                                          │
│  ┌─────────────────────────────────────────┐        │
│  │  _cache:                               │        │
│  │  ├── bucket[0]: SEL → IMP               │        │
│  │  ├── bucket[1]: SEL → IMP               │        │
│  │  ├── ...                               │        │
│  │  └── mask: hash mask                    │        │
│  └──────────────────────────────────────────┘        │
│                                                        │
│  第 2 步:在 Method List 中查找(线性搜索)            │
│  ┌───────────────────────────────────────────┐       │
│  │  class_getInstanceMethod(cls, SEL):       │       │
│  │  • 遍历 class 的 method list                │       │
│  │  • 如果找到 → 返回 Method                  │       │
│  │  • 如果没找到 → 进入 super class             │       │
│  │  • 直到找到或到达 NSObject                    │       │
│  └──────────────────────────────────────────┘       │
│                                                        │
│  第 3 步:动态解析(resolver 阶段)                    │
│  ┌────────────────────────────────────────┐          │
│  │  class_getInstanceMethod(cls, SEL):   │          │
│  │  • 调用 +resolveInstanceMethod:         │          │
│  │  • 如果方法被动态添加 → 重新查找          │          │
│  │  • 如果没添加 → 进入第 4 步              │          │
│  └──────────────────────────────────────┘          │
│                                                        │
│  第 4 步:消息转发(Forwarding)                       │
│  ┌───────────────────────────────────────────────┐   │
│  │  4a. -forwardingTargetForSelector:             │   │
│  │     → 将消息转发给其他对象处理                   │   │
│  │     → 如果返回非 nil → 结束                     │   │
│  │                                               │   │
│  │  4b. -methodSignatureForSelector:              │   │
│  │     → 生成方法签名                              │   │
│  │     → 如果返回 nil → +doesNotRecognizeSelector │   │
│  │                                               │   │
│  │  4c. -forwardInvocation:                       │   │
│  │     → 最终的消息转发                            │   │
│  │     → 可自定义处理逻辑                          │   │
│  └───────────────────────────────────────────────┘   │
│                                                        │
│  第 5 步:执行 IMP                                    │
│  ┌────────────────────────────────────────┐          │
│  │  IMP imp = class_getMethodImplementation │          │
│  │  imp(obj, @selector(method), args...)  │          │
│  └──────────────────────────────────────┘          │
└──────────────────────────────────────────────────────┘

性能分析:
┌─────────────────────────────────────────────────────────────────────┐
│  查找路径            │  平均耗时       │  命中率     │  适用场景        │
├───────────────────────┼─────────────────┼─────────────┼──────────────────┤
│ cache 查找          │  O(1)          │  99%+     │  常用方法        │
│ method list 查找    │  O(n)          │  1-5%     │  不常用方法      │
│ resolver 阶段        │  O(m)          │  <1%      │  动态添加方法    │
│ forwarding 阶段     │  O(p)          │  <1%      │  消息转发        │
└─────────────────────────────────────────┴───────────────┴──────────────┘
*/

2.2 objc_msgSend 汇编级分析 ​

asm
/*
objc_msgSend 汇编流程(64-bit,ARM64):

    // r0 = receiver (self/对象)
    // x1 = SEL (方法选择器)
    // x2...x7 = 参数
    // x0 = 返回值

    // 1. 获取 receiver 的 isa 指针
    ldr   x16, [x0]          // x16 = isa

    // 2. 从 isa 中获取 class 指针(提取 class 部分)
    and   x16, x16, #0xfffffffffffffc00  // 清除 isa 的低位标志位

    // 3. 从 class 的 cache 中查找 SEL
    ldp   x17, x16, [x16, #CACHE]   // x17 = 缓存指针, x16 = 方法 IMP
    b     __objc_msgForward     // 如果 cache miss,跳转到转发流程

    // 4. 计算缓存 bucket 索引
    and   x17, x1, x17.mask   // x1 = SEL, mask 用于哈希

    // 5. 检查 bucket 是否命中
    cmp   x17.key, x1          // key 是否等于 SEL

    // 6. 如果命中,执行 IMP
    b.eq  __objc_msgForward     // 未命中,转发
    mov   x16, IMP             // x16 = IMP
    br    x16                   // br = branch register,跳转到 IMP

    // 7. 缓存 miss,执行 class_getMethodImplementation
    bl    _class_getMethodAndForwardImp

    // 8. 执行 IMP
    // IMP 签名:void (*)(id, SEL, ...)
    // x0 = receiver
    // x1 = SEL
    // x2-x7 = 参数

    // 缓存结构(64-bit):
    // bucket[0]: key (SEL) | imp (IMP)
    // bucket[1]: key (SEL) | imp (IMP)
    // ...
    // mask: 哈希掩码
*/

// IMP 函数签名:
// typedef id (*IMP)(id self, SEL _cmd, ...);

// 等价 C 代码:
id objc_msgSend(id receiver, SEL selector, ...) {
    // 1. 获取 class
    Class cls = object_getClass(receiver);
    
    // 2. 查找 IMP
    IMP imp = class_getMethodImplementation(cls, selector);
    
    // 3. 缓存优化(实际在汇编层)
    // 4. 执行 IMP
    return imp(receiver, selector, ...);
}

3. 动态绑定与转发 ​

3.1 动态方法解析 ​

objc
/*
动态方法解析(+resolveInstanceMethod:):

原理:在消息发送的第 3 阶段(resolver),Runtime 会调用
+resolveInstanceMethod: 方法,允许开发者动态添加方法。

适用场景:
• 动态创建方法(根据 runtime 参数)
• 热修复(动态注入新方法)
• 代理模式优化(动态转发代理消息)
• AOP(面向切面编程)

代码示例:
*/

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    NSString *selName = NSStringFromSelector(sel);
    
    if ([selName hasPrefix:@"dynamicMethod_"]) {
        // 动态创建方法实现
        IMP imp = imp_implementationWithBlock(^(id self) {
            NSLog(@"动态方法被调用!");
            return @"动态返回值";
        });
        
        // 添加方法到类
        class_addMethod([self class], sel, imp, "@@:");
        return YES;
    }
    
    return [super resolveInstanceMethod:sel];
}

/*
方法签名:
@ = id (self)
@ = SEL (_cmd)
: = 参数(每个参数一个冒号)

示例:
- (void)foo: (int)x bar: (NSString *)y  →  "@v@:i@:"
- (NSString *)foo                      →  "@@"
- (void)foo                            →  "@v@:"
- (void)foo: (int)x                    →  "@v@:i"
*/

// 热修复示例(动态替换方法)
+ (void)hotFix {
    Class cls = [self class];
    
    SEL originalSel = @selector(originalMethod);
    SEL swizzledSel = @selector(hotFixedMethod);
    
    // 1. 获取原始方法
    Method originalMethod = class_getInstanceMethod(cls, originalSel);
    
    // 2. 获取新方法的 IMP
    Method newMethod = class_getInstanceMethod(cls, swizzledSel);
    
    // 3. 交换方法实现
    method_exchangeImplementations(originalMethod, newMethod);
}

3.2 消息转发全链路 ​

objc
/*
消息转发(Message Forwarding)的三个阶段:

┌───────────────────────────────────────────────────────────┐
│  Stage 1: dynamicMethodResolution                    │
│                                                      │
│  +resolveInstanceMethod: (实例方法)                    │
│  +resolveClassMethod:    (类方法)                      │
│                                                      │
│  作用:动态添加方法                                     │
│  返回 YES → Runtime 重新查找方法                        │
│  返回 NO  → 进入 Stage 2                             │
│                                                      │
│  ────────────────────────────────────────────────      │
│                                                      │
│  Stage 2: forwardingTargetForSelector              │
│                                                      │
│  -forwardingTargetForSelector:                      │
│                                                      │
│  作用:将消息转发给其他对象处理                          │
│  返回非 nil → 目标对象处理消息                           │
│  返回 nil  → 进入 Stage 3                             │
│                                                      │
│  常见用法:                                            │
│  • 转发给代理对象                                      │
│  • 转发给其他对象(消息转送)                          │
│  • KVO 实现                                            │
│  ────────────────────────────────────────────────      │
│                                                      │
│  Stage 3: forwardInvocation                         │
│                                                      │
│  1. -methodSignatureForSelector: 生成签名              │
│     → 返回 nil → doesNotRecognizeSelector            │
│     → 返回签名 → 创建 NSInvocation 对象              │
│                                                      │
│  2. -forwardInvocation: 处理消息                      │
│     → 可自定义处理逻辑                                  │
│     → 可转发给多个对象                                  │
│     → 可实现 AOP、装饰器模式等                          │
│                                                      │
│  NSInvocation 结构:                                  │
│  ┌────────────────────────────────────────────┐      │
│  │  methodSignature  — 方法签名                   │      │
│  │  target            — 目标对象                   │      │
│  │  selector         — 方法选择器                  │      │
│  │  arguments        — 参数数组                    │      │
│  │  returnValue      — 返回值                     │      │
│  └──────────────────────────────────────────┘      │
│                                                      │
│  适用场景:                                          │
│  • 协议方法可选实现(不实现也不会崩溃)                │
│  • 消息路由/转发                                      │
│  • AOP(面向切面编程)                                │
│  • 动态代理                                           │
│  • 跨对象方法调用                                    │
│                                                      │
└─────────────────────────────────────────────────────┘

// 完整转发示例:
@implementation MyForwarder

// Stage 1: 动态解析
+ (BOOL)resolveInstanceMethod:(SEL)sel {
    // 可以动态添加方法
    return NO; // 不处理,进入 Stage 2
}

// Stage 2: 转发给其他对象
- (id)forwardingTargetForSelector:(SEL)aSelector {
    NSString *selName = NSStringFromSelector(aSelector);
    
    // 转发给代理对象
    if ([selName hasPrefix:@"proxy_"]) {
        return self.delegate;
    }
    
    // 转发给 helper
    if ([selName hasPrefix:@"helper_"]) {
        return [MyHelper sharedHelper];
    }
    
    return nil; // 不处理,进入 Stage 3
}

// Stage 3: 最终转发
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    // 生成签名
    NSString *selName = NSStringFromSelector(aSelector);
    
    if ([selName hasPrefix:@"optional_"]) {
        // 可选方法 - 生成一个无操作签名
        return [NSMethodSignature signatureWithObjCTypes:"v@:"];
    }
    
    // 转发给目标
    if ([aSelector respondsToSelector:@selector(helperMethod:)]) {
        return [self.helper methodSignatureForSelector:aSelector];
    }
    
    return [super methodSignatureForSelector:aSelector];
}

- (void)forwardInvocation:(NSInvocation *)anInvocation {
    SEL sel = anInvocation.selector;
    
    if ([self.helper respondsToSelector:sel]) {
        [anInvocation invokeWithTarget:self.helper];
    }
}

@end

4. Method Swizzling 全栈 ​

4.1 Method Swizzling 原理 ​

objc
/*
Method Swizzling 原理:

通过交换两个方法的 IMP(实现),改变方法调用行为。

IMP 是函数指针:
typedef id (*IMP)(id self, SEL _cmd, ...);

┌─────────────────────────────────────────────────────────────┐
│  原始状态:                                                 │
│  ┌────────────────────────────────────────────────────┐    │
│  │  Method List                                         │    │
│  │  ├─ "viewWillAppear:"  → IMP_A                      │    │
│  │  └─ "customMethod"     → IMP_B                      │    │
│  └────────────────────────────────────────────────┘    │
│  调用 "viewWillAppear:" → 执行 IMP_A                    │
│                                                        │
│  Swizzling 后:                                       │
│  ┌────────────────────────────────────────────────────┐    │
│  │  Method List                                         │    │
│  │  ├─ "viewWillAppear:"  → IMP_B  ⬅️ IMP 已交换      │    │
│  │  └─ "customMethod"     → IMP_A  ⬅️ IMP 已交换      │    │
│  └────────────────────────────────────────────────┘    │
│  调用 "viewWillAppear:" → 执行 IMP_B                     │
│  调用 "customMethod" → 执行 IMP_A                        │
│                                                        │
│  关键点:                                                │
│  • IMP 是函数指针,交换后调用行为永久改变                  │
│  • 通过 swizzling 后的方法名调用原方法(IMP 已交换)     │
│  • 线程安全:必须在 +load 中执行                         │
│  • 全局性:所有该类的实例都会受影响                       │
└───────────────────────────────────────────────────────┘
*/

// Method Swizzling 标准实现:

@interface MyViewController ()
@end

@implementation MyViewController

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class cls = [self class];
        
        SEL originalSel = @selector(viewWillAppear:);
        SEL swizzledSel = @selector(my_viewWillAppear:);
        
        Method originalMethod = class_getInstanceMethod(cls, originalSel);
        Method swizzledMethod = class_getInstanceMethod(cls, swizzledSel);
        
        // 先尝试添加 swizzled 方法(如果原方法不存在)
        BOOL didAdd = class_addMethod(cls,
            originalSel,
            method_getImplementation(swizzledMethod),
            method_getTypeEncoding(swizzledMethod));
        
        if (didAdd) {
            // 原方法不存在,把 swizzled 实现绑定到 original selector
            class_replaceMethod(cls,
                swizzledSel,
                method_getImplementation(originalMethod),
                method_getTypeEncoding(originalMethod));
        } else {
            // 两个方法都存在,交换 IMP
            method_exchangeImplementations(originalMethod, swizzledMethod);
        }
    });
}

- (void)my_viewWillAppear:(BOOL)animated {
    NSLog(@"Custom viewWillAppear called!");
    
    // 调用原方法(IMP 已交换,所以调用 my_viewWillAppear 实际执行的是原方法)
    [self my_viewWillAppear:animated];
}

@end

/*
⚠️ 常见陷阱:

1. +load vs +initialize
   • +load 在类加载时调用(线程安全,保证只执行一次)
   • +initialize 在第一次发送消息时调用(可能多次调用,不安全)
   • ✅ 始终使用 +load

2. 方法不存在的情况
   • class_addMethod 先尝试添加,避免崩溃
   • 检查 class_getInstanceMethod 返回值

3. 线程安全
   • dispatch_once 保证线程安全
   • 多个类 swizzling 需要各自 dispatch_once

4. 无限递归
   • swizzled 方法内部调用 self 的 swizzled 方法(IMP 已交换,实际执行的是原方法)
   • 不要调用 original selector 的名称(会造成无限循环)

5. 子类的影响
   • Swizzling 影响所有子类
   • 子类可能需要重新 swizzle
*/

4.2 安全的 Method Swizzling 封装 ​

objc
// 安全的 Swizzling 封装类
@interface Swizzler : NSObject
+ (void)swizzleClass:(Class)cls
         originalSEL:(SEL)originalSEL
        swizzledSEL:(SEL)swizzledSEL;
@end

@implementation Swizzler

static void swizzleMethod(Class cls, SEL original, SEL swizzled) {
    Method originalMethod = class_getInstanceMethod(cls, original);
    Method swizzledMethod = class_getInstanceMethod(cls, swizzled);
    
    if (!originalMethod || !swizzledMethod) return;
    
    IMP originalIMP = method_getImplementation(originalMethod);
    IMP swizzledIMP = method_getImplementation(swizzledMethod);
    
    const char *originalTypes = method_getTypeEncoding(originalMethod);
    const char *swizzledTypes = method_getTypeEncoding(swizzledMethod);
    
    BOOL didAdd = class_addMethod(cls,
        original, swizzledIMP, swizzledTypes);
    
    if (didAdd) {
        class_replaceMethod(cls, swizzled, originalIMP, originalTypes);
    } else {
        method_exchangeImplementations(originalMethod, swizzledMethod);
    }
}

+ (void)swizzleClass:(Class)cls
         originalSEL:(SEL)originalSEL
        swizzledSEL:(SEL)swizzledSEL {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        [self swizzleMethod:cls originalSEL:originalSEL swizzledSEL:swizzledSEL];
    });
}

@end

// 使用:
// [Swizzler swizzleClass:[MyViewController class]
//                  originalSel:@selector(viewWillAppear:)
//                 swizzledSel:@selector(my_viewWillAppear:)];

5. 分类(Category)与扩展(Extension)原理 ​

5.1 Category 底层原理 ​

objc
/*
Category 的内存布局与加载:

Category 数据结构(源码):
┌─────────────────────────────────────────────────────────┐
│  struct category_t {                                    │
│      const char *name;        // 类名                     │
│      struct class_t *cls;         // 原类                 │
│      struct method_list_t *instanceMethods;  // 实例方法   │
│      struct method_list_t *classMethods;     // 类方法     │
│      struct protocol_list_t *protocols;      // 协议       │
│      struct property_list_t *instanceProperties; // 属性   │
│  };                                                      │
│                                                         │
│  category_t 存储位置:                                    │
│  • __DATA/__objc_catlist section (Mach-O)              │
│  • 由 dyld 加载时解析                                   │
│  • 由 _objc_init 在 Runtime 初始化阶段注册              │
└────────────────────────────────────────────────────────┘

Category 加载流程(Runtime 初始化):
┌─────────────────────────────────────────────────────────┐
│  1. dyld 加载 category_t 数组                             │
│  ↓                                                       │
│  2. _read_images() 遍历所有 categories                    │
│  ↓                                                       │
│  3. 为每个 category 加载方法:                             │
│     • loadCategoryMethods()                              │
│     • 将 category 的方法添加到类的 method list           │
│     • 按加载顺序插入到 method list 的头部                  │
│  ↓                                                       │
│  4. 加载 protocol:                                      │
│     • loadCategoryProtocols()                            │
│     • 添加到类的 protocols 列表                          │
│  ↓                                                       │
│  5. 加载属性:                                            │
│     • loadCategoryProperties()                           │
│     • 添加到类的 ivar list                              │
│  ↓                                                       │
│  6. 方法查找优先级:                                      │
│     • Category 方法 > 原类方法 > 父类方法                  │
│     • 同 Category 中:最后加载的 Category 优先             │
└──────────────────────────────────────────────────────────┘

Category 与 原类方法的查找优先级:
┌──────────────────────────────────────────────────────┐
│  Category 中的方法      ⬆ 最高优先级                    │
│  Category 中的方法      ⬆                              │
│  ...                                                   │
│  原类的 @implementation 中的方法                     │
│  ...                                                   │
│  父类的方法            ⬇ 最低优先级                     │
└───────────────────────────────────────────────────────┘

⚠️ Category 方法覆盖原类方法的机制:
• Category 的方法通过 runtime 动态插入到 method list
• 插入位置:method list 的头部
• 消息发送时先遍历 method list,所以 Category 方法优先
• 如果两个 Category 都有同名方法 → 后加载的优先
*/

5.2 Category 的局限性 ​

objc
/*
Category 的局限性:
┌─────────────────────────────────────────────┐
│ 支持                                      │
├───────────────────────────────────────────┤
│ • 添加方法(实例方法 / 类方法)               │
│ • 添加协议                                 │
│ • 添加属性(需要关联对象)                   │
│                                           │
│ 不支持                                    │
│ • 添加实例变量(Ivar)                       │
│ • 存储数据(只能通过关联对象)                │
│ • 修改原类的方法实现(通过 swizzle)          │
│                                           │
│ 解决方案:                                  │
│ • 添加数据 → 使用 Associated Objects        │
│ • 修改实现 → 使用 Method Swizzling         │
│ • 添加 Ivar → 使用 Runtime 动态绑定        │
└────────────────────────────────────────┘

关联对象(Associated Objects)实现原理:
┌───────────────────────────────────────────────────────┐
│  objc_setAssociatedObject(obj, key, value, policy)   │
│                                                   │
│  内部实现:                                         │
│  ┌────────────────────────────────────────┐       │
│  │  AssociatedObjectMap                     │       │
│  │  ├── objc_object → [                    │       │
│  │  │   key → AssociatedObject {           │       │
│  │  │     value → ...                      │       │
│  │  │     policy → ...                     │       │
│  │  │   },                                │       │
│  │  │   key → ...                          │       │
│  │  └────────────────────────────────────────┘       │
│  │                                                   │
│  │  policy:                                        │
│  │  • OBJC_ASSOCIATION_ASSIGN          — 弱引用       │
│  │  • OBJC_ASSOCIATION_RETAIN_NONATOMIC — 强引用    │
│  │  • OBJC_ASSOCIATION_COPY          — 复制          │
│  │  • OBJC_ASSOCIATION_RETAIN        — 强引用        │
│  │  • OBJC_ASSOCIATION_COPY_NONATOMIC — 复制         │
│  └──────────────────────────────────────────────────┘
*/

// Category 添加属性的标准方式(关联对象):
@implementation UIView (MyExtension)

static char kBackgroundColorKey;

- (void)setMyBackgroundColor:(UIColor *)color {
    objc_setAssociatedObject(self, &kBackgroundColorKey, color, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (UIColor *)myBackgroundColor {
    return objc_getAssociatedObject(self, &kBackgroundColorKey);
}

@end

// 动态添加 Ivar 的高级技巧:
static char _dynamicIvarKey;

@implementation MyView

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Ivar ivar = ivar_addIvar([self class], "_myValue", sizeof(NSInteger), nil, "@");
        // 通过 KVC 访问
    });
}

- (NSInteger)myValue {
    return [self valueForKey:@"_myValue"] ?: @0;
}

- (void)setMyValue:(NSInteger)value {
    [self setValue:@(value) forKey:@"_myValue"];
}

@end

6. Protocol Witness Table ​

6.1 原理详解 ​

objc
/*
Protocol Witness Table(协议见证表):

Swift 协议与 ObjC 协议的根本区别:

┌───────────────────────────────────────────────────────┐
│                  协议实现方式                            │
├──────────────────────────────────────────────────┐   │
│                                                    │
│  ObjC Protocol:                                   │
│  • 运行时查找(method signature match)            │
│  • 通过 @protocol + @interface 声明                  │
│  • 通过 respondsToSelector: 检查                      │
│  • 动态分发(selector)                              │
│  • 性能:运行时查找,有开销                           │
│                                                    │
│  Swift Protocol(非 @objc):                        │
│  • 编译期绑定(Witness Table)                       │
│  • 通过 conforming types 声明                        │
│  • 通过 protocol witness table 静态查找               │
│  • 静态分发(零开销)                                │
│  • 性能:编译期确定,零开销                           │
│                                                    │
│  Witness Table 结构:                                │
│  ┌──────────────────────────────────────────┐      │
│  │  Protocol Witness Table                    │      │
│  │  ┌───────────────────────────────────┐    │      │
│  │  │  Type: MyClass                     │    │      │
│  │  │  → implements Protocol1:           │    │      │
│  │  │    ├─ method1 → MyClass.method1    │    │      │
│  │  │    ├─ method2 → MyClass.method2    │    │      │
│  │  │    └─ associatedType → MyClass.Item│    │      │
│  │  │  → implements Protocol2:           │    │      │
│  │  │    ├─ methodA → MyClass.methodA    │    │      │
│  │  │    └─ methodB → MyClass.methodB    │    │      │
│  │  └───────────────────────────────────┘    │      │
│  └──────────────────────────────────────────┘      │
│                                                    │
│  内存布局:                                         │
│  • Witness Table 存储在可执行文件的 __swift_protos 段  │
│  • 每个 conforming type 有一个 Witness Table       │
│  • Runtime 通过 Protocol 和 Witness Table 查找       │
│    实现方法                                          │
└───────────────────────────────────────────────┘
*/

6.2 Protocol 的动态分发 ​

objc
/*
Protocol 动态分发(Swift):

当使用 @objc protocol 时,Swift 会为协议生成 Objective-C 兼容的接口,
通过 selector 动态查找方法。

┌─────────────────────────────────────────────────┐
│  @objc protocol vs Swift protocol               │
│                                                 │
│  @objc protocol:                                │
│  ┌────────────────────────────────────────┐    │
│  │  • 运行时分发(selector 查找)             │    │
│  │  • 需要继承 NSObjectProtocol             │    │
│  │  • 方法参数必须是桥接兼容类型              │    │
│  │  • 通过 method signature match           │    │
│  │  • 生成 Objective-C 兼容接口              │    │
│  └────────────────────────────────────────┘    │
│                                                 │
│  Swift protocol(非 @objc):                     │
│  ┌────────────────────────────────────────┐    │
│  │  • 编译期分发(Witness Table)           │    │
│  │  • 不需要继承 NSObjectProtocol           │    │
│  │  • 可以使用泛型、关联类型                 │    │
│  │  • 通过 Witness Table 查找               │    │
│  │  • 零开销静态分发                         │    │
│  └────────────────────────────────────────┘    │
└────────────────────────────────────────────┘

Protocol Witness Table 查找过程:
1. 编译期:
   • 类型 conforming 协议 → 生成 Witness Table
   • Witness Table 存储在 __swift_protos 段
   • 每个方法映射到具体实现

2. 运行时:
   • 通过 Protocol 和 Witness Table 查找方法
   • 查找时间:O(1)(哈希表查找)
   • 零开销(编译期确定)

3. 泛型约束:
   • protocol<Protocol> 约束 → 编译期检查
   • @objc 协议约束 → 运行时检查
*/

7. Swift Runtime ​

7.1 Swift 类型系统 ​

objc
/*
Swift 类型系统 vs Objective-C 类型系统:

┌─────────────────────────────────────────────────────────────┐
│                    类型系统对比                               │
├──────────────────────────────────────────────────────────┤
│                                    │  ObjC                │
├──────────────────────────────────────────────────────────┤
│  类型安全                          │  ✅ (可选)             │
│                                    │  ✅ (强)              │
│  泛型                              │  ✅ (强,编译期)       │
│                                    │  ❌                   │
│  协议                              │  ✅ (Witness Table)    │
│                                    │  ✅ (protocol 对象)    │
│  值类型 vs 引用类型                 │  ✅ (struct/class)     │
│                                    │  ❌ (只有类)           │
│  内存管理                          │  ✅ (ARC)             │
│                                    │  ✅ (ARC/MRC)         │
│  元类型(Metatype)                │  ✅ (Type/Metatype)    │
│                                    │  ❌ (只有 Class)      │
│  函数类型                           │  ✅ (函数是对象)        │
│                                    │  ✅ (block)           │
└──────────────────────────────────────────────────────────┘

Swift Metatype:
┌─────────────────────────────────────────────────┐
│  Swift 元类型(Metatype)                          │
│                                                  │
│  类型信息(Type Metadata)存储在:                │
│  • _swift_reflection_tables                     │
│  • __swift_types 段(Mach-O)                   │
│                                                  │
│  元类型结构:                                     │
│  ┌─────────────────────────────────────────┐    │
│  │  Swift Metatype                           │    │
│  │  ┌────────────────────────────────────┐ │    │
│  │  │  TypeDescriptor                     │ │    │
│  │  │  • Name(名称)                      │ │    │
│  │  │  • Size(内存大小)                   │ │    │
│  │  │  • Alignment(内存对齐)              │ │    │
│  │  │  • Flags(标志位)                    │ │    │
│  │  │  • ProtocolConformance(协议一致性)  │ │    │
│  │  │  • MemberDescriptor(成员列表)       │ │    │
│  │  │  • SuperclassDescriptor(父类描述)    │ │    │
│  │  └────────────────────────────────────┘ │    │
│  └─────────────────────────────────────────┘    │
│                                                  │
│  Metatype 用法:                                │
│  • MyClass.self → 类对象                        │
│  • MyClass.Type → 元类型                        │
│  • NSStringFromClass(myClass) → 类名字符串        │
│  • class_getName(cls) → 类名字符串              │
│  • object_getClass(obj) → 动态类型              │
│  • type(of: obj) → 运行时类型                    │
└─────────────────────────────────────────────────┘
*/

7.2 Runtime 调试与调试技巧 ​

objc
// Runtime 调试工具:

// 1. 遍历类的所有方法
void printClassMethods(Class cls) {
    unsigned int methodCount;
    Method *methods = class_copyMethodList(cls, &methodCount);
    
    for (unsigned int i = 0; i < methodCount; i++) {
        Method method = methods[i];
        SEL sel = method_getName(method);
        const char *types = method_getTypeEncoding(method);
        NSLog(@"Method: %s (%s)", sel_getName(sel), types);
    }
    
    free(methods);
}

// 2. 遍历类的所有实例变量
void printClassIvars(Class cls) {
    unsigned int ivarCount;
    Ivar *ivars = class_copyIvarList(cls, &ivarCount);
    
    for (unsigned int i = 0; i < ivarCount; i++) {
        Ivar ivar = ivars[i];
        const char *name = ivar_getName(ivar);
        const char *type = ivar_getTypeEncoding(ivar);
        NSLog(@"Ivar: %s (%s)", name, type);
    }
    
    free(ivars);
}

// 3. 遍历类的协议
void printClassProtocols(Class cls) {
    unsigned int protocolCount;
    Protocol **protocols = class_copyProtocolList(cls, &protocolCount);
    
    for (unsigned int i = 0; i < protocolCount; i++) {
        Protocol *protocol = protocols[i];
        NSLog(@"Protocol: %s", protocol_getName(protocol));
    }
    
    free(protocols);
}

// 4. 检查对象是否能响应某个 selector
BOOL canRespondToSelector(id obj, SEL sel) {
    return [obj respondsToSelector:sel];
}

// 5. 获取类的实例大小
void printClassSize(Class cls) {
    NSLog(@"Instance size: %zu bytes", class_getInstanceSize(cls));
    NSLog(@"Aligned size: %zu bytes", class_getAlignedInstanceSize(cls));
    NSLog(@"Ivar layout: %@", class_getIvarLayout(cls) ? "Yes" : "No");
}

8. 面试题汇总 ​

高频面试题 ​

Q1: Runtime 的消息发送过程?

答:

  1. 从 receiver 的 isa 指针获取 class
  2. 在 class 的 cache 中查找 SEL(O(1) 哈希查找)
  3. 未命中 → 在 method list 中遍历查找
  4. 未找到 → 调用 +resolveInstanceMethod:(动态解析)
  5. 未处理 → 调用 -forwardingTargetForSelector:(转发)
  6. 未处理 → 生成 methodSignature → forwardInvocation(最终转发)
  7. 未处理 → +doesNotRecognizeSelector(崩溃)
  8. 找到 IMP → 执行

Q2: Method Swizzling 线程安全吗?

答:

  • Swizzling 本身不是线程安全的
  • 必须在 +load 中执行(系统保证线程安全)
  • 或者使用 dispatch_once 确保只执行一次
  • 多线程同时 swizzle 同一类可能导致混乱
  • ✅ 最佳实践:+load + dispatch_once

Q3: Category 与 Extension 的区别?

答:

  • Category:运行时加载,方法可以覆盖原类方法,不能添加 ivar,可以添加属性(通过关联对象)
  • Extension(匿名 Category):编译时加载,方法添加到原类 method list 的末尾,可以添加 ivar
  • 查找优先级:Extension > Category > 原类方法 > 父类方法

Q4: Protocol Witness Table 的工作原理?

答:

  • 编译期:类型 conforming 协议时生成 Witness Table
  • 存储在 __swift_protos 段
  • 运行时通过 Witness Table 查找方法实现(O(1) 哈希查找)
  • 零开销静态分发
  • 与 ObjC 协议(运行时查找)不同

Q5: Associated Objects 的实现原理?

答:

  • Runtime 维护一个全局的 AssociatedObjectMap
  • key 是 object 和 ivar key 的组合
  • policy 控制引用计数行为(retain/copy/assign)
  • 通过 objc_setAssociatedObject/objc_getAssociatedObject 访问

Q6: 为什么 Category 不能添加实例变量?

答:

  • Category 的内存布局中不包含 ivar 字段
  • Category 的方法通过 runtime 动态插入
  • 如果要添加数据存储,使用 Associated Objects(关联对象)

9. 参考资源 ​