三 、動態添加方法緩存
咱們能夠經過runtime動態地添加方法。那麼到底啥叫動態添加方法呢?動態添加方法就是當咱們程序運行時才知道咱們應該調用哪一個方法。咱們首先須要瞭解這一點,當咱們編寫完一段代碼後,咱們點擊run 的時候,編譯器會先進行預編譯、編譯、連接、運行這幾個步驟。C語言是再編譯的時候就已經肯定了函數的調用順序,而OC在編譯的時候,只是肯定了哪一個對象,發送什麼消息,具體這個消息能不能找到對應的方法還不知道,只有在運行時,才能肯定是否可以執行咱們所指望的方法。咱們如下面的代碼爲例:函數
[person test_inPerson]; // objc_msgSend(person, @selector(test_inPerson));上面的語句再編譯後獲得的就是這個函數。 // 經過編譯咱們可以知道三點:一、消息的接受者是person對象。二、須要執行名字爲test_inPerson 的方法 三、這個方法不帶參數
在上一篇博客中,我提到了SEL和IMP,可是因爲寫博客時已經太晚了,就沒有認真地解釋。spa
若是想了解方法調用的過程,恐怕咱們須要瞭解4個概念:(1)isa 指針。( 2)superclass 屬性 。 (3)SEL 。 ( 4)IMP。指針
1) isa 指針:指向對象的類的指針。code
2)superclass :指向父類。orm
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類中仍然找不到方法,則會報錯,找不到方法。 */
瞭解了方法調用的過程,下面咱們就來看看如何動態的添加方法。爲了可以表達清楚,特在此敬上代碼it
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執行了
說明咱們動態添加方法成功了。最後特別提示哦,若是函數和方法不是一回事,不要把他倆搞混了。方法是經過類或者對象調用的,而函數是能夠直接調用執行的。