iOS開發(fā)之 Method Swizzling 深入淺出

logo.jpeg

iOS開發(fā)之 Method Swizzling 深入淺出

只要善用Google鹃栽,網(wǎng)上有很多關(guān)于Method Swizzling的Demo宵蛀,在這里我就不打算貼代碼了蚯妇,主要介紹下概念松靡,原理键俱,注意事項等等。

開發(fā)需求

如果產(chǎn)品經(jīng)理突然說:"在所有頁面添加統(tǒng)計功能提佣,也就是用戶進(jìn)入這個頁面就統(tǒng)計一次"吮蛹。我們會想到下面的一些方法:

  • 手動添加

直接簡單粗暴的在每個控制器中加入統(tǒng)計,復(fù)制拌屏、粘貼潮针、復(fù)制、粘貼...
上面這種方法太Low了倚喂,消耗時間而且以后非常難以維護(hù)每篷,會讓后面的開發(fā)人員罵死的。

  • 繼承

我們可以使用繼承的方式來解決這個問題端圈。創(chuàng)建一個基類焦读,在這個基類中添加統(tǒng)計方法,其他類都繼承自這個基類舱权。

然而矗晃,這種方式修改還是很大,而且定制性很差宴倍。以后有新人加入之后张症,都要囑咐其繼承自這個基類仓技,所以這種方式并不可取。

  • Category

我們可以為UIViewController建一個Category俗他,然后在所有控制器中引入這個Category脖捻。當(dāng)然我們也可以添加一個PCH文件,然后將這個Category添加到PCH文件中兆衅。

  • Method Swizzling

我們可以使用蘋果的“黑魔法”Method Swizzling地沮,Method Swizzling本質(zhì)上就是對IMPSEL進(jìn)行交換。

先了解幾個概念

Selectors, Methods, & Implementations

Objective-C的運(yùn)行時中羡亩,selectors, methods, implementations 指代了不同概念摩疑,然而我們通常會說在消息發(fā)送過程中,這三個概念是可以相互轉(zhuǎn)換的畏铆。 下面是蘋果 Objective-C Runtime Reference中的描述:

  • Selector(typedef struct objc_selector *SEL):在運(yùn)行時 Selectors 用來代表一個方法的名字未荒。Selector是一個在運(yùn)行時被注冊(或映射)的C類型字符串。Selector由編譯器產(chǎn)生并且在當(dāng)類被加載進(jìn)內(nèi)存時由運(yùn)行時自動進(jìn)行名字和實現(xiàn)的映射及志。

  • Method(typedef struct objc_method *Method):方法是一個不透明的用來代表一個方法的定義的類型片排。

  • Implementation(typedef id (*IMP)(id, SEL,...)):這個數(shù)據(jù)類型指向一個方法的實現(xiàn)的最開始的地方。該方法為當(dāng)前CPU架構(gòu)使用標(biāo)準(zhǔn)的C方法調(diào)用來實現(xiàn)速侈。該方法的第一個參數(shù)指向調(diào)用方法的自身(即內(nèi)存中類的實例對象率寡,若是調(diào)用類方法,該指針則是指向元類對象(metaclass)倚搬。第二個參數(shù)是這個方法的名字selector冶共,該方法的真正參數(shù)緊隨其后。

理解 selector, method, implementation 這三個概念之間關(guān)系的最好方式是:在運(yùn)行時每界,類(Class)維護(hù)了一個消息分發(fā)列表來解決消息的正確發(fā)送捅僵。每一個消息列表的入口是一個方法(Method),這個方法映射了一對鍵值對眨层,其中鍵值是這個方法的名字 selector(SEL)庙楚,值是指向這個方法實現(xiàn)的函數(shù)指針 implementation(IMP)Method swizzling 修改了類的消息分發(fā)列表使得已經(jīng)存在的 selector 映射了另一個實現(xiàn) implementation趴樱,同時重命名了原生方法的實現(xiàn)為一個新的 selector馒闷。

Method Swizzling原理

Method Swizzing是發(fā)生在運(yùn)行時的,主要用于在運(yùn)行時將兩個Method進(jìn)行交換叁征,我們可以將Method Swizzling代碼寫到任何地方纳账,但是只有在這段Method Swilzzling代碼執(zhí)行完畢之后互換才起作用。

image
image

Method Swizzling 使用注意

類簇設(shè)計模式

在iOS中NSNumber捺疼、NSArray疏虫、NSDictionary等這些類都是類簇(Class Clusters),一個NSArray的實現(xiàn)可能由多個類組成。
所以如果想對NSArray進(jìn)行Swizzling卧秘,必須獲取到其“真身”進(jìn)行Swizzling尤蛮,直接對NSArray進(jìn)行操作是無效的。

下面列舉了NSArray和NSDictionary本類的類名斯议,可以通過Runtime函數(shù)取出本類。

類名 真身
NSArray __NSArrayI
NSMutableArray __NSArrayM
NSDictionary __NSDictionaryI
NSMutableDictionary __NSDictionaryM

注意要點

  • Swizzling應(yīng)該總在+load中執(zhí)行
  • Swizzling應(yīng)該總是在dispatch_once中執(zhí)行
  • Swizzling在+load中執(zhí)行時醇锚,不要調(diào)用[super load]哼御。如果多次調(diào)用了[super load],可能會出現(xiàn)“Swizzle無效”的假象焊唬,原理見下圖:
image

Swift 自定義類中使用 Method Swizzling

要在 Swift 自定義類中使用 Method Swizzling 有兩個必要條件:

  • 包含 Swizzle 方法的類需要繼承自 NSObject
  • 需要 Swizzle 的方法必須有動態(tài)屬性(dynamic attribute)

注:對于 Swift 的自定義類恋昼,因為默認(rèn)并沒有使用 Objective-C 運(yùn)行時,因此也沒有動態(tài)派發(fā)的方法列表赶促,所以如果要 Swizzle 的是 Swift 類型的方法的話液肌,是需要將原方法和替換方法都加上 dynamic 標(biāo)記,以指明它們需要使用動態(tài)派發(fā)機(jī)制鸥滨。當(dāng)然類也要繼承自 NSObject嗦哆。

再注:下面這個例子使用了 Objective-C 的動態(tài)派發(fā),對于 NSObject 的子類(UIViewController)是可以直接使用的婿滓,并不是 Swift 中自定義的類老速,因此沒有加 dynamic 標(biāo)記也是可以的。

Method Swizzling 中 Objective-C 與 Swift 的異同

區(qū)別 Objective-C Swift
Runtime 頭文件 #import <objc/runtime.h> 不需要
Swizzling 調(diào)用處 load 方法 initialize 方法

注:load 方法只在 Objective-C 里有凸主,而且不能在 Swift 里重載橘券,不管怎么試都會報編譯錯誤。接下來執(zhí)行 Swizzle 最好的地方就是 initialize了卿吐,這是調(diào)用第一個方法前的地方旁舰。

因為 Swizzling 會改變?nèi)譅顟B(tài),所以我們需要在運(yùn)行時采取一些預(yù)防措施嗡官。GCD 的dispatch_once 可以保證操作的原子性箭窜,確保代碼只被執(zhí)行一次,不管有多少個線程衍腥。

Method Swizzling 實際應(yīng)用

APM(應(yīng)用性能管理)

網(wǎng)絡(luò)監(jiān)控的原理绽快,應(yīng)該就是hook NSURLConnection , NSURLSession。崩潰收集的原理紧阔,應(yīng)該就是hook NSException坊罢。

國外資料 ??

可能需要梯子

小廣告 ??

GitHub開源了一款iOS調(diào)試小工具,功能之一就是實現(xiàn)網(wǎng)絡(luò)請求抓包(代碼零入侵),原理也是使用了
Method Swizzling, 感興趣的童鞋可以進(jìn)來看看, 也歡迎使用?? http://DotzuX.com

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末擅耽,一起剝皮案震驚了整個濱河市活孩,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌乖仇,老刑警劉巖憾儒,帶你破解...
    沈念sama閱讀 221,576評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件询兴,死亡現(xiàn)場離奇詭異,居然都是意外死亡起趾,警方通過查閱死者的電腦和手機(jī)诗舰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來训裆,“玉大人眶根,你說我怎么就攤上這事”吡穑” “怎么了属百?”我有些...
    開封第一講書人閱讀 168,017評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長变姨。 經(jīng)常有香客問我族扰,道長,這世上最難降的妖魔是什么定欧? 我笑而不...
    開封第一講書人閱讀 59,626評論 1 296
  • 正文 為了忘掉前任渔呵,我火速辦了婚禮,結(jié)果婚禮上砍鸠,老公的妹妹穿的比我還像新娘厘肮。我一直安慰自己,他們只是感情好睦番,可當(dāng)我...
    茶點故事閱讀 68,625評論 6 397
  • 文/花漫 我一把揭開白布类茂。 她就那樣靜靜地躺著,像睡著了一般托嚣。 火紅的嫁衣襯著肌膚如雪巩检。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,255評論 1 308
  • 那天示启,我揣著相機(jī)與錄音兢哭,去河邊找鬼。 笑死夫嗓,一個胖子當(dāng)著我的面吹牛迟螺,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播舍咖,決...
    沈念sama閱讀 40,825評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼矩父,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了排霉?” 一聲冷哼從身側(cè)響起窍株,我...
    開封第一講書人閱讀 39,729評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后球订,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體后裸,經(jīng)...
    沈念sama閱讀 46,271評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,363評論 3 340
  • 正文 我和宋清朗相戀三年冒滩,在試婚紗的時候發(fā)現(xiàn)自己被綠了微驶。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,498評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡开睡,死狀恐怖因苹,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情士八,我是刑警寧澤,帶...
    沈念sama閱讀 36,183評論 5 350
  • 正文 年R本政府宣布梁呈,位于F島的核電站婚度,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏官卡。R本人自食惡果不足惜蝗茁,卻給世界環(huán)境...
    茶點故事閱讀 41,867評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望寻咒。 院中可真熱鬧哮翘,春花似錦、人聲如沸毛秘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽叫挟。三九已至艰匙,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間抹恳,已是汗流浹背员凝。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留奋献,地道東北人健霹。 一個月前我還...
    沈念sama閱讀 48,906評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像瓶蚂,于是被迫代替她去往敵國和親糖埋。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,507評論 2 359