Objective-C Runtime(四)isa swizzling
Runtime 4 isa swizzling
- 簡介
- 物件、類的結構
- objc_object
- objc_class
- 訊息傳遞(Messaging)
- objc_method
- objc_msgSend
- 動態方法解析和轉發
- 動態方法解析
- 快速訊息轉發
- 標準訊息轉發
- 訊息轉發與多繼承
- 訊息轉發與代理物件
- Method Swizzling
- class_replaceMethod
- method_setImplementation
- method_exchangeImplementations
- Method Swizzling 的應用
- Method Swizzling 注意事項
- isa swizzling
- 介紹
- 應用之KVO
- 注意
持續更新中...
介紹
對比上一篇 Runtime 3 Method Swizzling, isa swizzling 顧名思義就是把物件的 isa 指標進行替換。
根據第一篇 Runtime 1 簡介,物件、類的結構,訊息傳遞,我們知道物件都有 isa 指標指向它的類,訊息傳遞時也通過isa指標找到類中所對應的方法。更改物件的 isa 指標,不僅改變了它所屬於的型別,也更改了它的行為(方法)。
舉個例子:
@interface Father: NSObject
@property (nonatomic, assign) NSInteger f;
@end
@implementation Father
@end
@interface Child: Father
@property (nonatomic, assign) NSInteger c;
@end
@implementation Child
@end
然後執行:
//1
Child *child = [[Child alloc] init];
Class class = object_getClass(child); //Child
//2
NSLog(@"%ld, %ld", child.c, child.f); //0, 0
//3
object_setClass(child, [NSObject class]);
class = object_getClass(child); //NSObject
//4
NSLog(@"%ld, %ld", child.c, child.f); //error: -[NSObject c]: unrecognized selector sent to instance 0x60400002aaa0
上面的程式碼就是將 child 物件進行 isa swizzling,具體步驟分析如下:
- 上面程式碼建立了例項 child,它的 isa 指向 Child 類。
-
child.c
和child.f
是通過訊息傳遞找到方法實現的,通過 child 的 isa 指標找到它的 Child 類,然後在 Child 類中的 method list 找 c。f 方法的查詢同理,只是多了一步在 Child 類中找不到,則往它的 superclass 中找。 -
object_setClass(child, [NSObject class])
方法將 child 物件的 isa 指向 NSObject。 - 這時再進行訊息傳遞時,查詢的是 child 物件的 isa 指向的 NSObject 類,由於 NSObject 類沒有對應的 c 和 f 方法,最終找不到方法程式崩潰。
應用之KVO
KVO在呼叫存取方法之前總是呼叫 willChangeValueForKey: ,之後總是呼叫 didChangeValueForkey: 。怎麼做到的呢?答案是通過 isa 混寫(isa-swizzling)。
Apple 的文件對 KVO 實現的描述:
Automatic key-value observing is implemented using a technique called isa-swizzling.
...
When an observer is registered for an attribute of an object the isa pointer of the observed object is modified, pointing to an intermediate class rather than at the true class.
...
從Apple 的文件可以看出:Apple 並不希望過多暴露 KVO 的實現細節。不過,要是藉助 runtime 提供的方法去深入挖掘,所有被掩蓋的細節都會原形畢露:
當你觀察一個物件時,一個新的類會被動態建立。這個類繼承自該物件的原本的類,並重寫了被觀察屬性的 setter 方法。重寫的 setter 方法會負責在呼叫原 setter 方法之前和之後,通知所有觀察物件:值的更改。最後通過 isa 混寫(isa-swizzling) 把這個物件的 isa 指標 ( isa 指標告訴 Runtime 系統這個物件的類是什麼 ) 指向這個新建立的子類,物件就神奇的變成了新建立的子類的例項。我畫了一張示意圖,如下所示:
然而 KVO 在實現中使用了 isa-swizzling 的確不是很容易發現:Apple 還重寫了-class
方法並返回原來的類。企圖欺騙我們:這個類沒有變,就是原本那個類。。。如下:
Father *father = [[Father alloc] init];
[father addObserver:self forKeyPath:@"f" options:NSKeyValueObservingOptionNew context:nil];
NSLog(@"%@", object_getClass(father)); //NSKVONotifying_Father
NSLog(@"%@", father.class); //Father
假設“被監聽的物件”的類物件是 MYClass ,有時候我們能看到對 NSKVONotifying_MYClass 的引用而不是對 MYClass 的引用。藉此我們得以知道 Apple 使用了 isa 混寫(isa-swizzling)。具體探究過程可參考 這篇博文 。
由下面執行過程可知,通過KVO生成的中間類繼承原來的類:
Class kvoClass = object_getClass(father);
Class kvoSuperClass = class_getSuperclass(kvoClass);
NSLog(@"%@", kvoSuperClass); //Father
注意
isa-swizzling 改變了物件說屬型別,因此更改範圍比 method-swizzling 範圍更廣,使用時要更加註意。
runtime 極其尖銳,選擇使用 runtime 時要清楚每一步的真正原理。
相關文章
- Objective-C Runtime 執行時之四:Method SwizzlingObject
- Objective-C Runtime (三):Method Swizzling(方法替換)Object
- 神經病院Objective-C Runtime入院第一天—isa和ClassObject
- ISA Swizzling 《iOS程式設計實戰》iOS程式設計
- Runtime - 基於isa-swizzling實現訊息監聽,擴充套件響應式框架套件框架
- Runtime中的 isa 結構體結構體
- Objective-C RuntimeObject
- iOS 瞭解isa-swizzling (類指標交換)iOS指標
- OC isa結構、訊息傳遞、Method Swizzling
- 深入理解Objective-C-Runtime-isaObject
- 理解 OBJECTIVE-C RUNTIMEObject
- Objective-C Method Swizzling 的最佳實踐Object
- 深入理解Objective-C RuntimeObject
- Objective-C執行時特性:Method Swizzling魔法Object
- iOS 開發:『Runtime』詳解(二)Method SwizzlingiOS
- Runtime Method Swizzling開發例項彙總
- Objective-C Runtime (一):類與物件Object物件
- 詳解 Objective-C 中的 RuntimeObject
- ios runtime之Method Swizzling及其應用場景iOS
- Objective-C runtime 拾遺 (四)—— 不常用的程式/執行緒通訊方法Object執行緒
- 深入理解 Objective-C Runtime 機制Object
- iOS Runtime簡單介紹,以及不同類的Method SwizzlingiOS
- Objective-C Runtime 1小時入門教程Object
- Objective-C Runtime (二):方法與訊息轉發Object
- Objective-C 的底層實現(Runtime)(轉載)Object
- Objective-C runtime 拾遺 (二)——Log message sendObject
- Objective-C Runtime 執行時之六:拾遺Object
- 死磕Objective-C runtime執行時之一Object
- Objective-C Runtime 之動態方法解析實踐Object
- runtime執行時 isa指標 SEL方法選擇器 IMP函式指標 Method方法 runtime訊息機制 runtime的使用指標函式
- 神經病院 Objective-C Runtime 出院第三天——如何正確使用 RuntimeObject
- 神經病院Objective-C Runtime出院第三天——如何正確使用RuntimeObject
- Objective-C Runtime 執行時之一:類與物件Object物件
- Objective-C Runtime 執行時之三:方法與訊息Object
- iOS之runtime詳解api(四)iOSAPI
- Objective-C Runtime 執行時之五:協議與分類Object協議
- isa指標指標
- 通過Runtime原始碼瞭解Objective-C中的方法儲存原始碼Object