Runtime的總結(第二篇)

利用Runtime實現方法交換

分析場景 ? ??在我們的項目開發中,會遇到這樣的問題:如果字符串鏈接里面帶有文字,那它返回的url是空的, “null” ,后面我們再進行網絡請求一個空的url結果..... 。這也就說明NSURL 在創建出來后,系統不會檢測是否為nil。怎么解決?

開始的想法:我可以為NSURL擴展一個方法,在方法實現里判斷 返回的url是否為空,再做一些其他處理,最后再把方法名替換下就行啦。 ? 擴展的方法如下;

+(instancetype)HZ_urlWtihString:(NSString *)str{

NSURL *url = [NSURL URLWithString:str];

if (url == nil) {

NSLog(@"url 空了");

}

return url;

}

要是方法調用次數少,簡單替換下方法名 。但是如果類似于這種情況 并且替換的地方很多呢?就要一個一個找替換 很麻煩。 ?

這時有一個一勞永逸的方法:HOOK(面向切面編程)鉤子思想!!?勾住某個方法的調用,改變方法調用的順序。

繼續說,我們可以不改變系統方法的調用,還是調用URLWithString,但是調用后讓它走到我們自定義方法的實現里。 方法的調用就是發送消息 , 那么調用URLWithString方法 被編譯后C++代碼 就是這樣: ??objc_msgSend(<#id self#>, @selector(URLWithString)) ,只要他發送這消息,我就攔截到 ?,接下來就需要我們做些改變 ??! Runtime 提供了改變方法調用順序的功能 ?,那改變的是什么? ?@selector(URLWithString)這是被編譯后的我們無法改變(改變相當于改變源碼),在@selector之后還有一個機會改變方法的調用?,改變的是方法的IMP

提示方法的組成:SEL方法編號+IMP方法實現(實質:函數指針,它指向一塊內存區域,內存里面的二進制指令)),SEL和IMP是一一對應的。 也可以這樣理解:SEL(書的目錄)--IMP(書的頁碼)-- ?代碼(書里的內容);我們發送消息SEL 它就會找到IMP(IMP不是代碼 它是函數指針)


發送消息就是通過目錄找頁碼,通過頁碼找內容

Runtime可以這么做,它可以改變目錄上面的對應關系,它可以做交換


那什么時候交換?要在一切調用該方法之前做好,main函數里一般不寫,我們可以在分類的.m源文件里,源文件再加載的時候有個+(void)load方法,

//App裝載進內存的時候 就會執行這個指令;(app安裝在手機里都是二進制,點開App第一件事就是將硬盤里的二進制裝進內存里,就會讀load里面的內容,也就是先執行load里面的指令,再啟動APP 執行UIApplicationmain, load比main函數靠前)

+(void)load{

//下鉤子

}

我們在load里面“下鉤子”。我們會用到這個方法來交換:

method_exchangeImplementations(<#Method m1#>, <#Method m2#>)//改變方法實現

我們通過函數獲取m1 m2

+(void)load{

//下鉤子

//? ? class_getInstanceMethod;//獲取實例方法

//獲取類方法,返回Method結構體

Method urlWithString = class_getClassMethod(self, @selector(URLWithString:));

Method HZ_UrlWithString = class_getClassMethod(self, @selector(HZ_urlWtihString:));

//交換方法的實現(相當于交換:“書的頁碼”)

method_exchangeImplementations(urlWithString, HZ_UrlWithString);

}

到現在,方法交換就實現啦,原來的代碼不用動。但是還有一地方沒改,否則會造成循環調用 ,棧溢出:URLWithString 改為HZ_urlWtihString

//HZ_urlWtihString 與 urlWithString 的imp已經交換

+(instancetype)HZ_urlWtihString:(NSString *)str{

NSURL *url = [NSURL HZ_urlWtihString:str];

if (url == nil) {

NSLog(@"url 空了");

}

return url;

}

所謂HOOK,是一種思想,是專門的一種鉤子,他會在內存中找到某一個方法的調用(一塊兒內存區域),直到調用這個方法時,就勾住他,對它的方法行為進行改變,所以Runtime 的方法交換 某種意義上說也是一種鉤子。

動態添加方法

當一個類被調用了沒有被實現的方法,那么他會走這兩種方法的一種

//當類被調用沒有實現的類方法時,調用該方法

+(BOOL)resolveClassMethod:(SEL)sel{

}

//當這個類被調用沒有實現的對象方法時,調用該方法

+(BOOL)resolveInstanceMethod:(SEL)sel{

}

比如在控制器里,一個類Person被調用了一個沒有實現的無參數方法[p performSelector:@selector(eat)];,會走到resolveInstanceMethod:里來,那么我們就可以在該方法中給Person類動態添加方法。使用Runtime添加方法,就會用到這個函數class_addMethod,代碼實例

//當這個類被調用沒有實現的對象方法時,調用該方法

+(BOOL)resolveInstanceMethod:(SEL)sel{

NSLog(@"%@",NSStringFromSelector(sel));

//動態添加方法(方法組成:SEL (方法編號) + IMP(函數指針) + 函數體)

/*

1.cls 目標類

2.sel 方法編號

3.imp 方法實現(函數指針)

4.返回值類型 (無返回值無參的函數 為 "v@:")//v代表viod,@代表一個id對象,:代表方法編號

*/

class_addMethod(self, sel, (IMP)haha, "v@:");

//不知道返回什么,父類返回什么 我這就返回什么

return [super resolveInstanceMethod:sel];

}

//寫一個函數,haha 就是函數指針,這里需要加(IMP)來強制轉化,不然會報警告

void haha(){

NSLog(@"來了!!");

}

以上時添加的無返回值無參數的實例方法,如果調用了一個沒有實現的帶參數的方法,

[p performSelector:@selector(eat) withObject:@"哈哈"];

這就要改變class_addMethod的第四個參數-返回值類型,和haha函數體里的參數。返回值類型改為“v@:”,更改函數體里的參數,有兩個 方法調用的隱式參數?id self,SEL _cmd,

/*

1.方法的調用者

2.方法的編號

(OC 里 每個方法調用,它的函數體里有倆個隱式參數,是默認的)

*/

void eat(id self,SEL _cmd,NSString *str){

NSLog(@"%@",str);

}

為什么會有隱式參數,可能隱約感覺的到,方法調用就是消息發送:

// [p performSelector:@selector(eat) withObject:@"哈哈"];

//調用OC方法的時候,會給IMP傳入兩個參數:方法的調用者 ?id self 、方法編號SEL _cmd (行參名字可以改)

? ? objc_msgSend(p, @selector(eat:),@"哈哈");

這里面的p 和?@selector(eat:) 就傳到?void eat(id self,SEL _cmd,NSString *str)這里來啦。

*另外swift調用方法 和oc ?調用方法的底層是不一樣的。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,363評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,497評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,305評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,962評論 1 311
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,727評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,193評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,257評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,411評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,945評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,777評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,978評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,519評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,216評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,642評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,878評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,657評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,960評論 2 373

推薦閱讀更多精彩內容

  • 轉至元數據結尾創建: 董瀟偉,最新修改于: 十二月 23, 2016 轉至元數據起始第一章:isa和Class一....
    40c0490e5268閱讀 1,751評論 0 9
  • 我們常常會聽說 Objective-C 是一門動態語言,那么這個「動態」表現在哪呢?我想最主要的表現就是 Obje...
    Ethan_Struggle閱讀 2,223評論 0 7
  • 這篇文章完全是基于南峰子老師博客的轉載 這篇文章完全是基于南峰子老師博客的轉載 這篇文章完全是基于南峰子老師博客的...
    西木閱讀 30,578評論 33 466
  • Zeppelin介紹 Apache Zeppelin提供了web版的類似ipython的notebook,用于做數...
    JasonDing閱讀 3,676評論 1 14
  • 遇到一個相互喜歡的人是很難的 去喜歡一個人也是很難的 不適合談戀愛的我注定是孤獨的 因為不懂得喜歡也不值得被喜歡 ...
    有一顆文藝心的理科女閱讀 299評論 3 2