iOS 進階開發 runtime(三),ios開發runtime
三 、動態添加方法
我們可以通過runtime動態地添加方法。那麼到底啥叫動態添加方法呢?動態添加方法就是當我們程式運行時才知道我們應該調用哪個方法。我們首先需要瞭解這一點,當我們編寫完一段代碼後,我們點擊run 的時候,編譯器會先進行先行編譯、編譯、連結、運行這幾個步驟。C語言是再編譯的時候就已經確定了函數的調用順序,而OC在編譯的時候,只是確定了哪個對象,發送什麼訊息,具體這個訊息能不能找到對應的方法還不知道,只有在運行時,才能確定是否能夠執行我們所期望的方法。我們以下面的代碼為例:
[person test_inPerson];// objc_msgSend(person, @selector(test_inPerson));上面的語句再編譯後得到的就是這個函數。 // 通過編譯我們能夠知道三點:1、訊息的接受者是person對象。2、需要執行名字為test_inPerson 的方法 3、這個方法不帶參數
在上一篇部落格中,我提到了SEL和IMP,但是由於寫部落格時已經太晚了,就沒有認真地解釋。
如果想瞭解方法調用的過程,恐怕我們需要瞭解4個概念:(1)isa 指標。( 2)superclass 屬性 。 (3)SEL 。 ( 4)IMP。
1) isa 指標:指向對象的類的指標。
2)superclass :指向父類。
3)SEL :選取器,是根據方法名字產生的ID, 每個selector其實是一個char*類型,記錄對應IMP的位置。SEL列表本身是一個雜湊儲存的set集合,尋找起來非常高效。
4) IMP:函數指標。
下面我們就來說一下方法調用的過程。
/* 調用方法 */ [person test_inPerson]; /* 轉換成訊息 objc_msgSend(person, @selector(test_inPerson));上面的語句再編譯後得到的就是這個函數。 */ /* 1)檢查 是否selector 2)檢查person 是否為空白,如果為空白的話就把selector也置為空白,這樣的話相當於什麼也不做,當然也不報錯 3)根據SEL 尋找IMP。首先從緩衝中尋找,看看緩衝中是否存在SEL對應的IMP。如果存在則執行,否則繼續下一步。 4)根據SEL 和 isa 指標再IMP 列表中尋找對應的IMP。如果找到則執行,否則執行下一步。 5)根據superclass 和SEL 尋找父類的IMP 如果找到則執行。否則繼續執行這一步,直到NSObject 類。 6)如果再NSObject類中仍然找不到方法,則會報錯,找不到方法。 */
瞭解了方法調用的過程,下面我們就來看看如何動態添加方法。為了能夠表達清楚,特在此敬上代碼
DZLPerson *person=[[DZLPerson alloc] init];//發送訊息想要執行名字為test0的方法,但是我們person類及其分類中並沒有該方法的實現[person performSelector:@selector(test0)];
#import "DZLPerson.h"#import <objc/runtime.h>@implementation DZLPerson/* 注意 這是函數 不是方法。函數是不能通過方法調用的。 */void test0(){ NSLog(@"test0 執行了");}/*如果找不到類方法則調用該方法,決定是否動態地添加方法 *///+(BOOL)resolveClassMethod:(SEL)sel//{// return BOOL;//}/* 如果找不到執行個體方法則調用該方法,決定是否動態地添加方法 */+(BOOL)resolveInstanceMethod:(SEL)sel{ // 如果找不到的方法時test0 的話 if ([NSStringFromSelector(sel) isEqualToString:@"test0"]) { //添加方法。其實就是將現有的函數實現(IMP) 和 SEL進行串連。 class_addMethod(self,sel,test0,"v@:"); } return YES;}@end
列印結果為
2015-04-13 22:43:06.406 runtime講解[12452:693059] test0執行了
說明我們動態添加方法成功了。最後特別提示哦,如果函數和方法不是一回事,不要把他倆搞混了。方法是通過類或者對象調用的,而函數是可以直接調用執行的。