Runtime 是一套 純 C 語言 API 库,構成 Objective-C 的動態運行基礎。所有 OC 代碼最終編譯為 Runtime C 函數呼叫:
// OC 代碼
[[MJPerson alloc] init]
// 編譯後等價於
objc_msgSend(objc_msgSend(@\"MJPerson\", @\"alloc\"), @\"init\")
#import
#import
// 核心函數
id objc_msgSend(id self, SEL op, ...); // 消息發送
Method *class_copyMethodList(Class cls, unsigned int *outCount); // 遍歷方法
Ivar *class_copyIvarList(Class cls, unsigned int *outCount); // 遍歷成員變量
objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount); // 遍歷屬性
Class objc_allocateClassPair(Class superclass, const char *name, size_t extraBytes); // 動態建立類
void class_addMethod(Class cls, SEL name, IMP imp, const char *types); // 動態添加方法
void class_replaceMethod(Class cls, SEL name, IMP imp, const char *types); // 替換方法
void object_setClass(id object, Class cls); // 修改對象 isa (KVO 核心)
objc_setAssociatedObject / objc_getAssociatedObject // 關聯對象
| 類型 | 含義 | 用途 |
|---|---|---|
| Class | 類對象指標 | 獲取類資訊、動態建立類 |
| SEL | 選子 (方法名唯一標識) | 方法查找、動態呼叫 |
| IMP | 方法實現函數指標 | 方法轉發、攔截、替換 |
| Ivar | 成員變量資訊 | 遍歷 ivar、KVC 底層 |
| Method | 方法資訊 | 遍歷方法、交換實現 |
| objc_property_t | 屬性資訊 | 遍歷屬性、自動歸檔 |
@implementation PYPerson
- (void)encodeWithCoder:(NSCoder *)encoder {
unsigned int count = 0;
Ivar *ivars = class_copyIvarList([self class], &count);
for (int i = 0; i < count; i++) {
Ivar ivar = ivars[i];
const char *name = ivar_getName(ivar);
NSString *key = [NSString stringWithUTF8String:name];
id value = [self valueForKey:key];
[encoder encodeObject:value forKey:key];
}
free(ivars);
}
- (instancetype)initWithCoder:(NSCoder *)decoder {
if (self = [super init]) {
unsigned int count = 0;
Ivar *ivars = class_copyIvarList([self class], &count);
for (int i = 0; i < count; i++) {
Ivar ivar = ivars[i];
const char *name = ivar_getName(ivar);
NSString *key = [NSString stringWithUTF8String:name];
id value = [decoder decodeObjectForKey:key];
[self setValue:value forKey:key];
}
free(ivars);
}
return self;
}
@end
- (instancetype)initWithDictionary:(NSDictionary *)dict {
if (self = [super init]) {
unsigned int count = 0;
objc_property_t *props = class_copyPropertyList([self class], &count);
for (int i = 0; i < count; i++) {
objc_property_t prop = props[i];
const char *name = property_getName(prop);
NSString *key = [NSString stringWithUTF8String:name];
id value = dict[key];
if (value) [self setValue:value forKey:key];
}
free(props);
}
return self;
}
// 觀察者註冊時,Runtime 會:
// 1. 動態建立原類的子類 (NSKVONotifying_OriginalClass)
// 2. 修改被觀察對象的 isa 指向新子類
// 3. 在子類中重寫 setter 方法:
// - 呼叫 willChangeValueForKey:
// - 呼叫原 setter
// - 呼叫 didChangeValueForKey:
// 4. 刪除觀察時恢復 isa (若無其他觀察者)
Class originalClass = object_getClass(observedObject);
Class kvoClass = objc_allocateClassPair(originalClass,
[NSStringFromClass(originalClass) stringByAppendingString:@\"_KVO\"].UTF8String, 0);
// 重寫 setter...
class_addMethod(kvoClass, @selector(setName:), (IMP)newSetter, \"v@:@\");
object_setClass(observedObject, kvoClass);
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
SEL original = @selector(viewWillAppear:);
SEL swizzled = @selector(xxx_viewWillAppear:);
Method origMethod = class_getInstanceMethod(class, original);
Method swizMethod = class_getInstanceMethod(class, swizzled);
method_exchangeImplementations(origMethod, swizMethod);
});
}
- (void)xxx_viewWillAppear:(BOOL)animated {
// 埋點邏輯
[Tracker trackPageView:NSStringFromClass([self class])];
// 呼叫原實現 (實際上是交換後的 original)
[self xxx_viewWillAppear:animated];
}
「Runtime 是 OC 的靈魂——靜態語言的安全與動態語言的靈活,在此達到微妙平衡。」