Runtime 是 Objective-C 動態特性的核心。面試大廠必問,實戰 Swizzling、動態屬性、協議實現皆建立於此。本文從源碼級視角剖析 objc_msgSend、isa/元類體系、方法緩存、消息轉發三階段。
objc_msgSend// 編譯器將 [obj makeText] 轉為:
objc_msgSend(obj, @selector(makeText));
彙編級實現 (arm64 簡化):
objc_msgSend:
// 1. 取 receiver 的 isa
ldr x13, [x0] // x13 = obj->isa
// 2. 從 isa 取類對象 (非 metaclass)
and x13, x13, #0x7FFFFFFFFFF8 // 去除 tagged pointer 位
// 3. 查找緩存
ldr x12, [x13, #CACHE_OFFSET]
// ... cache lookup ...
// 4. 緩存未命中 → __objc_msgSend_uncached
b __objc_msgSend_uncached
struct objc_class {
Class isa; // → metaclass
Class super_class; // → 父類
const char *name; // 類名
long version, info; // 版本/標志
long instance_size; // 實例大小
struct objc_ivar_list *ivars; // 實例變量列表
struct objc_method_list **methodLists; // 方法列表數組
struct objc_cache *cache; // 方法緩存 (SEL→IMP)
struct objc_protocol_list *protocols;
};
實例 obj → isa → 類對象 (Class) → 存儲實例方法、實例變量
類對象 (Class) → isa → 元類 (Metaclass) → 存儲類方法、類變量
元類 (Metaclass) → isa → 根元類 (NSObject meta) → isa 指向自身
根類 (NSObject) → super_class = nil
關鍵點:
objc_class 結構體,元類也是同一結構體,區別在 info 位標志 (CLS_CLASS vs CLS_META)。isa 指向類對象;類對象 isa 指向元類;元類 isa 最終指向根元類,根元類 isa 指向自身。receiver->isa->cache → methodLists → super_class→cache → ...typedef struct objc_selector *SEL; 本質是 const char* 唯一化後的指針。@selector(methodName) 編譯期生成,同名方法同一 SEL (不區分參數類型)。receiver->isa 獲取類對象cache 中按 SEL 查找 IMP → 命中直接跳轉methodLists (按類別編譯順序) 查找 SEL → 找到則填入 cache 並跳轉super_class 遞歸向上查找,直到根類| 階段 | 方法 | 用途 | 返回 |
|---|---|---|---|
| 1. 動態解析 | +resolveInstanceMethod:+resolveClassMethod: | 運行時動態添加方法實現 (如 class_addMethod) | YES 重新嘗試發送 |
| 2. 備用接收者 | -forwardingTargetForSelector: | 將消息轉發給其他對象 (零開銷) | 目標對象或 nil |
| 3. 完整轉發 | -methodSignatureForSelector:-forwardInvocation: | 構造 NSInvocation 任意處理 (修改參數、返回值、延遲執行) | 無 |
objc_msgSend(nil, sel) 直接返回 0/nil/零結構體,無崩潰。objc_msgSendSuper(&super_struct, sel),從 super_class 開始查找。Method m1 = class_getInstanceMethod(cls, @selector(original));
Method m2 = class_getInstanceMethod(cls, @selector(swizzled));
method_exchangeImplementations(m1, m2); // 交換 IMP
原理:直接修改 methodLists 中的 IMP 指針,緩存會在下次查找時自動更新。
Objective-C 消息機制 = isa 指針鏈 + 緩存哈希表 + 線性列表掃描 + 三階段轉發。理解此流程,即可掌握 Swizzling、動態添加方法、協議轉發、性能優化 (預熱緩存) 等高階技術。