1. RunTime簡介
RunTime(運行時)是指程序在運行過程中動態管理類型、對象、方法等的機制。Objective-C?和?Swift?都擁有自己的運行時系統,但設計理念和實現方式有很大不同。理解?RunTime?的底層原理,是掌握?iOS?高級開發的關鍵。
2. Objective-C?RunTime底層原理
2.1 對象結構與isa指針
2.1.1 OC對象的本質
Objective-C?對象在底層以結構體的形式實現,其最核心的成員是 isa 指針。
struct objc_object {Class isa;
};
- objc_object?是所有?OC?對象的基類結構體,只包含 isa 指針。
- 每個實例對象的內存布局:isa?指針?+ 父類成員變量 + 本類成員變量(由編譯器自動生成)。
- Class?實際上是?objc_class?類型,指向類對象。
- 每個對象通過 isa 指針找到自己的類對象,類對象再通過 isa?指針找到元類對象。
2.1.2 isa指針的優化(isa_t)
isa 指針在?64 位系統下被設計為共用體(union),不僅存儲類對象地址,還包含如是否為?Tagged?Pointer、部分標志位等信息。引用計數等信息通常存儲在 side table 中,只有部分位(如?extra_rc)用于優化。
union isa_t {isa_t() { }isa_t(uintptr_t value) : bits(value) { }Class cls;uintptr_t bits;struct {uintptr_t nonpointer : 1;uintptr_t has_assoc : 1;uintptr_t has_cxx_dtor : 1;uintptr_t shiftcls : 33;uintptr_t magic : 6;uintptr_t weakly_referenced : 1;uintptr_t deallocating : 1;uintptr_t has_sidetable_rc : 1;uintptr_t extra_rc : 19;};
};
- nonpointer:是否為優化過的isa指針
- shiftcls:類對象地址
- extra_rc:引用計數
- has_assoc:是否有關聯對象
Tagged Pointer:對于部分小對象(如小范圍的 NSNumber、NSDate),系統會直接將值編碼在指針中,從而提升性能。
2.1.3 OC對象的內存布局
實際內存布局順序為:
- isa指針(對象頭部,8字節/4字節,取決于架構)
- 父類的成員變量(從NSObject/父類開始,依次向下排列)
- 本類聲明的成員變量(按聲明順序排列)
以下代碼為例:
@interface Animal : NSObject {int age;int height;
}
@end@interface Dog : Animal {int weight;int color;
}
@end
Dog對象的內存布局:
| isa | age?| height?| weight?| color?|
|-----|-------|----------|-----------|--------|
- isa:指向類對象
- age、height:Animal父類成員變量
- weight、color:Dog本類成員變量
這樣設計可以保證子類對象在作為父類對象使用時,父類成員變量的偏移量不變,從而保證繼承的兼容性和對象模型的穩定性。
2.2 類對象與元類對象
2.2.1 類對象(Class)
類對象本質也是一個結構體,定義如下(簡化版):
struct objc_class : objc_object {Class isa;Class super_class;cache_t cache;class_data_bits_t bits;// 下面是bits指向的數據結構(class_rw_t/class_ro_t),用于描述類的詳細信息// 這里只做偽代碼展示struct class_rw_t {uint32_t flags; // 標志位uint32_t version; // 版本號const class_ro_t *ro; // 指向只讀數據method_list_t *methods; // 方法列表property_list_t *properties; // 屬性列表protocol_list_t *protocols; // 協議列表ivar_list_t *ivars; // 成員變量列表// ... 其他運行時擴展信息};struct class_ro_t {uint32_t flags; // 標志位uint32_t instanceStart; // 實例對象起始偏移uint32_t instanceSize; // 實例對象大小const char *name; // 類名method_list_t *methods; // 方法列表property_list_t *properties; // 屬性列表protocol_list_t *protocols; // 協議列表ivar_list_t *ivars; // 成員變量列表// ... 其他只讀信息};
};
- isa:指向元類對象
- super_class:指向父類對象
- cache:方法緩存
- class_data_bits_t:存儲方法列表、屬性列表、協議列表等
2.2.2 元類對象(Meta-Class)
- 元類對象用于存儲類方法的實現,本質上類方法就是元類的實例方法。
- 元類的isa指向根元類(NSObject的Meta-Class)
- 元類的super_class指向父類的元類
- 根元類的isa指針指向自己
2.2.3 內存結構圖
- 類對象和元類對象本質上都是objc_class結構體
- 類對象存儲實例方法,元類對象存儲類方法
2.3 方法緩存與方法列表
2.3.1 方法緩存(cache_t)
每個類對象都有一個方法緩存cache,結構如下:
struct cache_t {struct bucket_t *_buckets;mask_t _mask;mask_t _occupied;
};
- _buckets:哈希表,存儲 SEL(方法選擇子)和 IMP(實現指針)的映射關系。
- _mask、_occupied:用于哈希沖突處理
作用:加速方法查找,避免每次都遍歷方法列表。
2.3.2 方法列表
類對象中有方法列表,存儲所有實例方法:
struct method_t {SEL name; // 方法名const char *types; // 方法類型編碼IMP imp; // 方法實現指針
};
- 方法查找順序:先查緩存,再查方法列表,最后查父類
2.4 成員變量與屬性
- 成員變量(Ivar)
在類對象的?ivar_list?中,保存的是所有成員變量的描述信息(如名字、類型、偏移量等),而成員變量的實際值是存儲在每個實例對象的內存空間中(緊跟在 isa 指針之后,按照繼承鏈和聲明順序排列)。
- 屬性(Property)
屬性信息存儲在類對象的?property_list?中,僅用于描述屬性的名稱、類型、特性等。屬性通常會自動合成一個對應的成員變量(如?name),成員變量的實際值存儲在實例對象中。
// 成員變量描述結構體
struct ivar_t {int32_t *offset; // 偏移量(成員變量在實例對象內存中的位置)const char *name; // 成員變量名const char *type; // 類型編碼// ... 其他信息
};// 成員變量列表
struct ivar_list_t {uint32_t count; // 成員變量數量ivar_t ivars[]; // 成員變量數組
};// 屬性描述結構體
struct property_t {const char *name; // 屬性名const char *attributes; // 屬性特性字符串(如T@"NSString",C,N,V_name等)// ... 其他信息
};// 屬性列表
struct property_list_t {uint32_t count; // 屬性數量property_t properties[];// 屬性數組
};
3. Objective-C消息機制與方法查找——RunTime的靈魂
3.1 objc_msgSend流程
Objective-C?的方法調用本質上是“向對象發送消息”,而?objc_msgSend?就是消息發送的核心函數。理解其底層流程,是掌握?RunTime?的關鍵。
1. 空對象判斷(Nil?Check)
- 首先判斷消息接收者(對象指針)是否為 nil。
- 如果是?nil,objc_msgSend?直接返回?0,不做任何操作(這也是 OC?調用?nil 對象不會崩潰的原因)。
2. 獲取類對象(Class?Lookup)
- 通過對象頭部的?isa 指針,獲取當前對象的類對象(Class)。
3. 方法緩存查找(Cache Lookup)
- 優先在類對象的緩存(cache_t)中查找方法實現(IMP)。
- 如果緩存命中,直接跳轉到對應的?IMP 執行,速度極快。
4. 方法列表查找(Method List Lookup)
- 如果緩存未命中,則遍歷類對象的方法列表(method_list_t),查找與?SEL(方法選擇子)匹配的方法。
- 找到后,將?SEL?和 IMP?加入緩存,便于下次快速查找。
5. 父類鏈查找(Superclass Lookup)
- 如果當前類的方法列表中仍未找到目標方法,則順著?super_class?指針,遞歸查找父類的方法緩存和方法列表,直到?NSObject?為止。
6. 動態方法解析(Dynamic?Method?Resolution)
- 如果在整個繼承鏈上都找不到目標方法,RunTime?會嘗試動態方法解析。
- 通過調用?+resolveInstanceMethod:?或?+resolveClassMethod:,允許開發者動態添加方法實現。
- 如果在回調中通過?class_addMethod?成功添加了方法,RunTime 會重新走一次方法查找流程。
7. 消息轉發機制(Message Forwarding)
- 如果動態方法解析仍未能處理該消息,進入消息轉發階段:
- 首先調用?-forwardingTargetForSelector:,允許指定其他對象響應該消息。
- 如果還未處理,則調用?-methodSignatureForSelector:?和?-forwardInvocation:,開發者可自定義消息處理邏輯。
8. 拋出異常(Unrecognized?Selector)
- 如果所有機制都未能處理該消息,RunTime?最終會拋出?unrecognized selector sent?to?instance?異常,導致程序崩潰。
3.2?消息轉發三步曲
當對象接收到一個未實現的方法調用時,RunTime?會依次經歷以下步驟:
- 動態方法解析(resolveInstanceMethod:、resolveClassMethod:)
- 快速消息轉發(forwardingTargetForSelector:)
- 完整消息轉發(methodSignatureForSelector:?&?forwardInvocation:)
下面我們逐步拆解每一步的底層實現。
3.2.1?動態方法解析(resolveInstanceMethod、resolveClassMethod)
底層原理:
當實例方法找不到時,RunTime 會調用?+resolveInstanceMethod:;類方法找不到時,調用?+resolveClassMethod:。你可以在這里通過?class_addMethod?動態添加方法。
代碼示例:
// 動態添加實例方法
+ (BOOL)resolveInstanceMethod:(SEL)sel {if (sel == @selector(test)) {class_addMethod(self, sel, (IMP)testIMP, "v@:");return YES;}return [super resolveInstanceMethod:sel];
}// 動態添加類方法
+ (BOOL)resolveClassMethod:(SEL)sel {if (sel == @selector(testClassMethod)) {Class metaClass = object_getClass(self); // 獲取元類class_addMethod(metaClass, sel, (IMP)testClassMethodIMP, "v@:");return YES;}return [super resolveClassMethod:sel];
}
- 如果返回 YES,RunTime?會重新查找方法并執行。
- 如果返回?NO?或未處理,進入下一步。
3.2.2?快速消息轉發(forwardingTargetForSelector:)
底層原理:
如果動態方法解析未能處理,RunTime 會調用實例方法?-forwardingTargetForSelector:。你可以返回一個“備選接收者”,RunTime 會將消息直接轉發給該對象。如果返回?nil,進入完整消息轉發。
代碼示例:
- (id)forwardingTargetForSelector:(SEL)aSelector {if (aSelector == @selector(test)) {return anotherObj; // 轉發給 anotherObj}return [super forwardingTargetForSelector:aSelector];
}
3.2.3?獲取方法簽名(methodSignatureForSelector:)
底層原理:
如果快速轉發未處理,RunTime 會調用?-methodSignatureForSelector:,詢問你“這個 SEL 對應的方法簽名是什么?”你需要返回一個 NSMethodSignature 對象,描述方法的參數和返回值類型。如果返回 nil,RunTime?會直接拋出異常。
代碼示例:
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {if (aSelector == @selector(test:)) {return [NSMethodSignature signatureWithObjCTypes:"v@:@"];}return [super methodSignatureForSelector:aSelector];
}
3.2.4?自定義消息處理(forwardInvocation:)
底層原理:
RunTime 會根據方法簽名創建一個 NSInvocation 對象,封裝原始的消息調用。然后調用?-forwardInvocation:,你可以在這里自定義如何處理這個消息,比如轉發給其他對象、修改參數、記錄日志等。如果你沒有處理,調用?[super forwardInvocation:],最終會拋出異常。
代碼示例:
- (void)forwardInvocation:(NSInvocation *)anInvocation {if ([anotherObj respondsToSelector:anInvocation.selector]) {[anInvocation invokeWithTarget:anotherObj];} else {[super forwardInvocation:anInvocation];}
}
3.3 拋出異常(unrecognized selector)
1. 底層原理
如果上述所有機制都未能處理該消息,RunTime?會調用?doesNotRecognizeSelector:,最終拋出?unrecognized selector sent to instance?異常,導致程序崩潰。
2. 異常信息解讀
當出現此異常時,Xcode控制臺會輸出類似如下信息:
Terminating app due to uncaught exception 'NSInvalidArgumentException',
reason: '-[MyClass test]: unrecognized selector sent to instance 0x12345678'
- MyClass:出錯的類名
- test:未識別的方法名
- 0x12345678:對象內存地址
4. Swift RunTime機制深度剖析
4.1 Swift對象模型:值類型與引用類型的本質
4.1.1 值類型與引用類型的區別
在Swift中,對象主要分為兩種類型:值類型和引用類型。這兩種類型在內存管理和使用方式上有著本質的區別:
- 值類型:包括struct和enum,數據直接存儲在變量本身,通常在棧上分配,內存布局緊湊,訪問效率高。賦值時進行值拷貝。
- 引用類型:主要指class,數據存儲在堆上,變量保存的是指向對象的指針。支持繼承和多態,賦值時傳遞引用。
4.1.2 Swift類對象的內存結構
Swift的類對象采用了現代化的內存布局設計,其結構如下:
| Metadata指針 | 屬性1 | 屬性2 | ... |
- Metadata指針:指向類型的元數據,類似于Objective-C的isa指針,但包含更豐富的信息
- 屬性區:按順序存儲對象的所有屬性值
4.1.3?繼承NSObject的Swift類的內存結構
繼承NSObject的Swift類同時具有Swift和Objective-C的特征
以下代碼為例:
class MyClass: NSObject {var property1: Intvar property2: String
}
其內存布局如下所示:
+------------------+------------------+------------------+------------------+
| isa指針(OC) | Metadata指針(Swift) | property1 | property2 |
+------------------+------------------+------------------+------------------+
- 對象頭部包含兩個指針: Objective-C的isa指針和Swift的Metadata指針。
- 屬性按照Swift的內存布局規則排列。
- 方法根據是否@objc決定存儲位置,objc方法:存儲在Objective-C的class_rw_t中,非@objc方法:存儲在Swift的虛表中。
這種混合結構讓繼承NSObject的Swift類既能保持Swift的高效,又能與Objective-C無縫交互。
4.1.4?Metadata元數據詳解
Metadata是Swift類型系統的核心,它記錄了類型在運行時的所有關鍵信息。以類(Class)為例,其簡化結構如下:
以類(Class)為例,簡化結構如下:
struct ClassMetadata {void *kind; // 類型標識(區分class/struct/enum等)void *superclass; // 父類的Metadata指針void *cacheData; // 方法緩存(加速查找)void *data; // 指向更詳細的類型描述// ... 其他字段
};
Metadata采用分層設計:基礎信息在前,詳細信息通過指針層層遞進。
1. 基礎層:包含類型標識、父類信息等基本信息
2.?擴展層:過data指針指向更詳細的信息,包括:
- vtable:虛函數表,存儲可重寫方法的實現指針
- witness table:協議表,存儲協議方法的實現
- 屬性描述:屬性名、類型、偏移量等
- 泛型信息:泛型參數的類型描述
4.2 Swift方法調用機制:靜態與動態的平衡
4.2.1 靜態派發:Swift的默認選擇
Swift默認采用靜態派發機制,這意味著:
- 方法調用在編譯期就確定了調用地址。調用時無需查找,直接跳轉
- 適用于struct、final class、private方法等場景
- 執行效率高,接近C語言的函數調用
4.2.2 動態派發:特定場景的選擇
在以下情況下,Swift會采用動態派發機制:
- 被dynamic修飾的方法
- 遵循@objc協議的方法
- 繼承自NSObject的類中的方法
- 協議的可選方法
動態派發通過vtable(虛函數表)或witness table(協議表)實現:
+-------------------+
| 方法1的指針(IMP) |
| 方法2的指針(IMP) |
| ... |
+-------------------+
4.2.3 方法查找流程對比
- 靜態派發:編譯期確定方法地址,直接調用方法實現,無需運行時查找。
- 動態派發:運行時查找vtable/witness table,獲取方法實現指針,調用方法實現。
4.2.4?與Objective-C的對比
- OC:所有方法調用都走objc_msgSend,完全動態派發,靈活性高,但性能開銷大。
- Swift:優先使用靜態派發,僅在必要時使用動態派發,性能更優,但動態性受限。
4.3 Swift的反射與元編程能力
4.3.1 Mirror反射API
Swift提供了Mirror類型用于運行時反射,可以獲取對象的屬性名、屬性值、類型等信息。
let mirror = Mirror(reflecting: someObject)
for child in mirror.children {print("屬性名: \(child.label), 屬性值: \(child.value)")
}
- 只能讀取對象信息,不能修改。
- 支持獲取屬性名、屬性值、類型等信息,使用簡單,但功能有限。
4.3.2 Swift RunTime的局限性
- 無法動態添加/替換方法:沒有公開的RunTime C API
- 類型信息有限:大部分類型信息在編譯期丟失
- 元編程能力弱:不支持元類、動態創建類等高級特性
4.3.3?Swift RunTime的設計哲學與優化
- 類型安全:所有類型信息盡量在編譯期確定,避免運行時錯誤
- 極致性能:靜態派發、緊湊內存布局、去虛表等優化
- 安全性高:避免了OC RunTime帶來的諸多安全隱患
總結一句話:
Swift 的 RunTime 機制以犧牲部分動態性為代價,換來了更高的類型安全和運行效率。
5. Objective-C與Swift RunTime機制對比
5.1 核心機制對比
Objective-C RunTime | Swift RunTime | |
---|---|---|
對象頭部 | isa指針,部分信息存side table | Metadata指針,引用計數直接存對象頭部 |
方法派發 | 動態派發(objc_msgSend ),全部運行時查找 | 靜態派發為主,動態派發僅限特殊場景 |
動態性 | 極強,支持動態添加/替換方法、類、屬性等 | 較弱,類型和方法多在編譯期確定 |
反射能力 | 強大,支持完整反射和元編程 | Mirror API有限,無法動態修改類型結構 |
內存布局 | 對象頭部+成員變量,side table存擴展信息 | Metadata指針+屬性區,布局緊湊 |
弱引用 | 全局weak表,side table輔助 | 全局weak表,直接與對象頭部配合 |
關聯對象 | 支持(依賴side table) | 不支持(標準庫無此機制) |
性能 | 動態性帶來一定性能損耗 | 靜態派發、內存優化,性能極高 |
安全性 | 動態性強但易出錯 | 類型安全,編譯期檢查,運行時更安全 |
混編支持 | 與C/Swift無縫混編 | 與OC混編需繼承NSObject ,部分特性受限 |
5.2 方法派發機制
-
Objective-C 所有方法調用都通過objc_msgSend來實現,也就是每次調用方法時,系統都會在運行時查找方法的具體實現。這種方式非常靈活,可以在運行時動態替換方法,但也帶來了一定的性能損耗。
-
Swift采用以靜態派發為主的混合派發機制。大多數方法在編譯期就確定了實現位置,只有少數特殊情況(如被dynamic修飾的方法、遵循@objc協議的方法、協議的可選方法、繼承自NSObject的類的方法)才會使用動態派發。動態派發通過vtable或witness table實現,雖然限制了動態性,但大大提升了性能。
5.3 動態性與反射
-
Objective-C具備極強的動態性。開發者可以在運行時動態添加或替換方法、創建新類、修改類結構,還可以通過完整的反射API查詢和操作類型信息。這些特性使其非常適合實現AOP、熱修復等高級功能。例如,可以通過class_addMethod動態添加方法,通過method_exchangeImplementations交換方法實現。
-
Swift的動態性相對較弱。大多數類型信息在編譯期就已確定,運行時修改能力有限。雖然提供了Mirror API用于查看對象屬性和類型信息,但無法動態修改類型結構。這種設計雖然限制了某些高級特性的實現,但更適合類型安全的業務開發。例如,可以通過Mirror(reflecting: object)查看對象屬性。
5.4 內存布局與side table
-
Objective-C的對象內存布局包含isa指針(8字節)和按聲明順序排列的成員變量。擴展信息(如引用計數、弱引用、關聯對象、方法緩存等)存儲在side table中。這種設計雖然減少了對象本身的內存占用,但訪問這些信息需要額外查找side table,增加了訪問成本。
-
Swift的對象內存布局包含Metadata指針和按聲明順序排列的屬性。引用計數直接存儲在對象頭部,弱引用與對象頭部配合,沒有公開的side table機制。這種設計雖然增加了對象本身的大小,但減少了訪問開銷,整體內存布局更緊湊高效。
5.5 實際開發影響
-
在框架開發中,Objective-C更適合需要高度動態性的場景,如AOP實現、熱修復系統、插件化架構、動態代理、KVO/KVC等。可以通過減少消息發送、緩存方法實現、優化內存布局、合理使用side table等方式優化性能。
-
Swift更適合高性能業務邏輯、類型安全API、并發編程、函數式編程等場景。可以通過利用靜態派發、優化值類型使用、減少動態派發、優化內存布局等方式提升性能。在混編開發中,需要明確邊界、合理分工、注意性能、統一規范,根據具體需求選擇合適的語言或混編方案。
6. RunTime的實際應用場景與代碼示例
6.1 Objective-C?RunTime常見用法
6.1.1 方法交換(Method?Swizzling)
場景:無侵入埋點、AOP、日志等。
示例:統計所有UIViewController的viewDidLoad調用。
#import <objc/runtime.h>@implementation UIViewController (Swizzling)+ (void)load {static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{Method original = class_getInstanceMethod(self, @selector(viewDidLoad));Method swizzled = class_getInstanceMethod(self, @selector(swizzled_viewDidLoad));method_exchangeImplementations(original, swizzled);});
}- (void)swizzled_viewDidLoad {NSLog(@"%@ did load", NSStringFromClass([self class]));[self swizzled_viewDidLoad]; // 實際調用原viewDidLoad
}@end
6.1.2 動態添加屬性(關聯對象)
場景:為Category動態添加屬性。
#import <objc/runtime.h>static const void *kNameKey = &kNameKey;@interface Person : NSObject
@end@implementation Person
@end@interface Person (Category)
@property (nonatomic, strong) NSString *name;
@end@implementation Person (Category)- (void)setName:(NSString *)name {objc_setAssociatedObject(self, kNameKey, name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSString *)name {return objc_getAssociatedObject(self, kNameKey);
}@end
6.1.3 自動歸檔與解檔
場景:自動實現NSCoding協議,無需手寫每個屬性。
#import <objc/runtime.h>@implementation Person- (void)encodeWithCoder:(NSCoder *)coder {unsigned int count = 0;Ivar *ivars = class_copyIvarList([self class], &count);for (int i = 0; i < count; i++) {Ivar ivar = ivars[i];NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];id value = [self valueForKey:key];[coder encodeObject:value forKey:key];}free(ivars);
}- (instancetype)initWithCoder:(NSCoder *)coder {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];NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];id value = [coder decodeObjectForKey:key];[self setValue:value forKey:key];}free(ivars);}return self;
}@end
6.1.4 消息轉發機制
場景:多重代理、消息中轉、容錯處理。
// 快速消息轉發
- (id)forwardingTargetForSelector:(SEL)aSelector {if (aSelector == @selector(testMethod)) {return self.anotherObject;}return [super forwardingTargetForSelector:aSelector];
}// 完整消息轉發
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {if (aSelector == @selector(testMethod)) {return [NSMethodSignature signatureWithObjCTypes:"v@:"];}return [super methodSignatureForSelector:aSelector];
}- (void)forwardInvocation:(NSInvocation *)anInvocation {if ([self.anotherObject respondsToSelector:anInvocation.selector]) {[anInvocation invokeWithTarget:self.anotherObject];} else {[super forwardInvocation:anInvocation];}
}
6.2 Swift RunTime常見用法
6.2.1 Mirror反射
場景:調試、日志、自動打印對象屬性。
struct Person {var name: Stringvar age: Int
}let p = Person(name: "Tom", age: 18)
let mirror = Mirror(reflecting: p)
for child in mirror.children {print("屬性名: \(child.label ?? ""), 屬性值: \(child.value)")
}
// 輸出:
// 屬性名: name, 屬性值: Tom
// 屬性名: age, 屬性值: 18
6.2.2 動態派發與@objc
場景:需要與OC交互、KVO、KVC、消息轉發等。
class Animal: NSObject {@objc dynamic func run() {print("Animal run")}
}class Dog: Animal {override func run() {print("Dog run")}
}let a: Animal = Dog()
a.run() // 輸出 Dog run,走動態派發
總結
RunTime(運行時)是iOS開發的核心機制,主要指程序在運行過程中動態管理類型、對象、方法等的能力。Objective-C的RunTime極為強大,支持動態派發、方法交換、動態添加屬性、消息轉發等,底層通過isa指針、類對象、元類對象、方法緩存等結構實現,所有方法調用都走objc_msgSend,具備極高的靈活性和反射能力,適合AOP、熱修復等高級玩法。Swift的RunTime則以類型安全和高性能為主,大部分方法靜態派發,只有特殊場景才動態查找,反射能力有限,無法動態修改類型結構。兩者對比,OC更動態、靈活,Swift更安全、高效。實際開發中,OC適合底層框架和需要動態性的場景,Swift則適合業務開發和性能敏感場合。理解RunTime原理,有助于開發者寫出更高效、靈活和安全的iOS代碼。
如果覺得本文對你有幫助,歡迎點贊、收藏、關注我,后續會持續分享更多?iOS?底層原理與實戰經驗