Objective-C 是一門高度動態的語言,將大量編譯期、鏈接期決策推遲到執行期。這套由 C 與組合語言編寫的 Runtime 庫,是 OC 物件模型的核心引擎。本文深入剖析消息機制、類/元類結構、方法快取、動態解析與 Method Swizzling 等核心機制。
[obj makeText] 編譯為 objc_msgSend(obj, @selector(makeText))。執行期動態查找,編譯期僅檢查聲明存在性。這意味著:
// NSObject 定義
@interface NSObject {
Class isa; // 指向其類物件
}
```
類物件 (Class) 結構
struct objc_class {
Class isa; // 指向元類
Class super_class; // 父類
const char *name; // 類名
long version; // 版本資訊
long info; // 標識位 (CLS_CLASS / CLS_META)
long instance_size; // 實例大小
struct objc_ivar_list *ivars; // 成員變量列表
struct objc_method_list **methodLists; // 方法列表
struct objc_cache *cache; // 方法快取
struct objc_protocol_list *protocols; // 協議列表
};
關鍵指標關係圖解
-、實例變數)+、靜態變數)這構成了雙層平行繼承體系:實例鏈與類/元類鏈。
id objc_msgSend(id self, SEL op, ...) {
// 1. 從 self->isa 取得類物件
Class cls = self->isa;
// 2. 先查 cache (最近使用方法)
IMP imp = cache_get(cls->cache, op);
if (imp) return imp(self, op, ...);
// 3. 再查 methodLists
imp = methodList_find(cls->methodLists, op);
if (imp) {
cache_add(cls->cache, op, imp);
return imp(self, op, ...);
}
// 4. 遞迴查 super_class
cls = cls->super_class;
// ... 重複 2-3 步驟
// 5. 找不到 → 進入消息轉發流程
return _objc_msgForward(self, op, ...);
}
const char* 指向唯一字串。同名方法共用一個 SEL,故 OC 不支援多載。id (*IMP)(id, SEL, ...)。id、isKindOfClass:)objc_msgSend)dlopen、NSBundle)利用 SEL→IMP 映射在執行期可變的特性,交換兩個方法的 IMP,實現「不繼承重寫、仍能呼叫原實現」的效果。
// 典型用法:在 Category 中交換系統方法
#import <objc/runtime.h>
@implementation UIViewController (Swizzling)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class cls = [self class];
SEL original = @selector(viewWillAppear:);
SEL swizzled = @selector(my_viewWillAppear:);
Method origMethod = class_getInstanceMethod(cls, original);
Method swizzledMethod = class_getInstanceMethod(cls, swizzled);
// 1. 確保原方法有實現(若無則添加)
BOOL added = class_addMethod(cls, original,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
if (added) {
class_replaceMethod(cls, swizzled,
method_getImplementation(origMethod),
method_getTypeEncoding(origMethod));
} else {
// 2. 直接交換 IMP
method_exchangeImplementations(origMethod, swizzledMethod);
}
});
}
- (void)my_viewWillAppear:(BOOL)animated {
// 執行前置邏輯
NSLog(@"Before viewWillAppear");
// 呼叫原實現(實際上調用的是 swizzled 後的 original)
[self my_viewWillAppear:animated];
// 執行後置邏輯
NSLog(@"After viewWillAppear");
}
@end
+load 中執行(保證單次、早期執行),避免競態條件。dispatch_once 確保線程安全。| 場景 | 關鍵 API | 說明 |
|---|---|---|
| 關聯物件 | objc_setAssociatedObject / objc_getAssociatedObject | 在 Category 中模擬屬性 |
| 動態新增方法 | class_addMethod, resolveInstanceMethod: | 實現 KVO、Core Data 動態存取 |
| 方法交換 | method_exchangeImplementations | AOP、埋點、熱修復 |
| 遍歷屬性/方法/協議 | class_copyPropertyList 等 | 自動序列化、字典轉模型 |
| 動態建立類 | objc_allocateClassPair, objc_registerClassPair | KVO 實現、Proxy 模式 |