首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

Objective-C Runtime 深度解析:動態特性核心機制與實戰應用

     編輯:關於C語言

Runtime 是什麼?

Runtime 是一套 純 C 語言 API 库,構成 Objective-C 的動態運行基礎。所有 OC 代碼最終編譯為 Runtime C 函數呼叫:

// OC 代碼
[[MJPerson alloc] init]

// 編譯後等價於
objc_msgSend(objc_msgSend(@\"MJPerson\", @\"alloc\"), @\"init\")

核心能力:編譯期無法實現的動態操作

  • ✅ 運行期動態建立類 (KVO 底層實現)
  • ✅ 動態新增/修改屬性、方法
  • ✅ 遍歷類的所有成員變量、方法 (歸檔解檔、字典轉模型)
  • ✅ 方法攔截與轉發 (AOP、熱修復、埋點)
  • ✅ 關聯對象 (分類中添加屬性)

必備頭文件與核心函數

#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屬性資訊遍歷屬性、自動歸檔

實戰案例一:自動歸檔解檔 (NSCoding)

@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;
}

實戰案例三:KVO 底層原理 — 動態建立子類

// 觀察者註冊時,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);

實戰案例四:方法交換 - AOP 埋點

+ (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];
}

學習路徑建議

  1. 基礎:理解 isa、SEL、IMP、消息發送流程
  2. 遍歷:熟練使用 class_copy*List 系列函數
  3. 動態:掌握 objc_allocateClassPair、class_addMethod、method_exchangeImplementations
  4. 進階:消息轉發 (forwardInvocation:)、關聯對象、Method Swizzling 最佳實踐
  5. 源碼:研讀 objc4 開源實現 (objc-msg-arm64.s, objc-runtime-new.mm)

避坑指南

  • ⚠️ class_copy*List 返回的陣列需 free()
  • ⚠️ Method Swizzling 必須在 +load 中執行並用 dispatch_once
  • ⚠️ 動態建立類需在註冊前完成 (objc_registerClassPair)
  • ⚠️ 關聯對象策略 (OBJC_ASSOCIATION_RETAIN_NONATOMIC 等) 需正確選擇
  • ⚠️ 不要在 Swizzling 方法中無限遞歸呼叫自己

「Runtime 是 OC 的靈魂——靜態語言的安全與動態語言的靈活,在此達到微妙平衡。」

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved