method swizzling

Objective-C對(duì)象收到消息之后,究竟會(huì)調(diào)用何種方法需要在運(yùn)行期才能解析出來阁将。那你也許會(huì)問:與給定的選擇子名稱相對(duì)應(yīng)的方法是不是也可以在運(yùn)行期改變呢变隔?沒錯(cuò),就是這樣茵宪。若能善用此特性最冰,則可發(fā)揮出巨大優(yōu)勢,因?yàn)槲覀兗炔恍枰创a稀火,也不需要通過繼承子類來覆寫方法就能改變這個(gè)類本身的功能暖哨。這樣一來,新功能將在本類的所有實(shí)例中生效凰狞,而不是僅限于覆寫了相關(guān)方法的那些子類實(shí)例篇裁。此方案經(jīng)常稱為“方法調(diào)配”(method swizzling)。

類的方法列表會(huì)把選擇子的名稱映射到相關(guān)的方法實(shí)現(xiàn)之上赡若,使得“動(dòng)態(tài)消息派發(fā)系統(tǒng)”能夠據(jù)此找到應(yīng)該調(diào)用的方法达布。這些方法均以函數(shù)指針的形式來表示,這種指針叫做IMP逾冬,其原型如下:

NSString類可以響應(yīng)lowercaseString黍聂、uppercaseString、capitalizedString等選擇子粉渠。這張映射表中的每個(gè)選擇子都映射到了不同的IMP之上分冈,如圖1所示圾另。

圖1-NSString類選擇子映射關(guān)系

Objective-C運(yùn)行期系統(tǒng)提供的幾個(gè)方法都能夠用來操作這張表霸株。開發(fā)者可以向其中新增選擇子,也可以改變某選擇子所對(duì)應(yīng)的方法實(shí)現(xiàn)集乔,還可以交換兩個(gè)選擇子所映射到的指針去件。經(jīng)過幾次操作之后坡椒,類的方法表就會(huì)變成圖2這個(gè)樣子。

圖2-操作后的NSString類選擇子映射關(guān)系

在新的映射表中尤溜,多了一個(gè)名為newSelector的選擇子倔叼,capitalizedString的實(shí)現(xiàn)也變了,而lowercaseString與uppercaseString的實(shí)現(xiàn)則互換了宫莱。上述修改均無須編寫子類丈攒,只要修改了“方法表”的布局,就會(huì)反映到程序中所有的NSString實(shí)例之上授霸。這下大家見識(shí)到此特性的強(qiáng)大之處了吧巡验?
本條將會(huì)談到如何互換兩個(gè)方法實(shí)現(xiàn)。通過此操作碘耳,可為已有方法添加新功能显设。不過在講解怎樣添加新功能之前,我們先來看看怎樣互換兩個(gè)已經(jīng)寫好的方法實(shí)現(xiàn)辛辨。想交換方法實(shí)現(xiàn)捕捂,可用下列函數(shù):

void method_exchangeImplementations(Method m1, Method m2);

此函數(shù)的兩個(gè)參數(shù)表示待交換的兩個(gè)方法實(shí)現(xiàn),而方法實(shí)現(xiàn)則可通過下列函數(shù)獲得:

Method class_getInstanceMethod(Class aClass,SEL aSelector);

此函數(shù)根據(jù)給定的選擇從類中取出與之相關(guān)的方法斗搞。執(zhí)行下列代碼指攒,即可交換前面提到的lowercaseString與uppercaseString方法實(shí)現(xiàn):

Method originalMethod = class_getInstanceMethod([NSString class], @selector(lowercaseString));
Method swappedMethod = class_getInstanceMethod([NSString class], @selector(my_lowercaseString));

method_exchangeImplementations(originalMethod, swappedMethod);

從現(xiàn)在開始,如果在NSString實(shí)例上調(diào)用lowercaseString榜旦,那么執(zhí)行的將是uppercaseString的原有實(shí)現(xiàn)幽七,反之亦然:

        NSString *lowercaseString = [string lowercaseString];
        NSLog(@"lowercaseString = %@",lowercaseString);
    // Output: lowercaseString = STRING  
        NSString *uppercaseString = [string uppercaseString];
        NSLog(@"uppercaseString = %@",uppercaseString);
    // Output: uppercaseString = string

筆者剛才向大家演示了如何交換兩個(gè)方法實(shí)現(xiàn),然而在實(shí)際應(yīng)用中溅呢,像這樣直接交換兩個(gè)方法實(shí)現(xiàn)的澡屡,意義并不大。因?yàn)閘owercaseString與uppercaseString這兩個(gè)方法已經(jīng)各自實(shí)現(xiàn)得很好了咐旧,沒必要再交換了驶鹉。但是,可以通過這一手段來為既有的方法實(shí)現(xiàn)增添新功能铣墨。比方說室埋,想要在調(diào)用lowercaseString時(shí)記錄某些信息,這時(shí)就可以通過交換方法實(shí)現(xiàn)來達(dá)成此目標(biāo)伊约。我們新編寫一個(gè)方法姚淆,在此方法中實(shí)現(xiàn)所需的附加功能,并調(diào)用原有實(shí)現(xiàn)屡律。
新方法可以添加至NSString的一個(gè)“分類”(category)中:

@interface NSString (myAdditions)

- (NSString *)my_lowercaseString;

@end

上述新方法將與原有的lowercaseString方法互換腌逢,交換之后的方法表如圖2-5所示。

圖3-交換lowercaseString與my_lowercaseString方法的實(shí)現(xiàn)

新方法的實(shí)現(xiàn)代碼可以這樣寫:

@implementation NSString (myAdditions)

- (NSString *)my_lowercaseString {
    NSString *lowercase = [self my_lowercaseString];
    NSLog(@" %@ ==> %@",self,lowercase);
    return lowercase;
}

@end

這段代碼看上去好像會(huì)陷入遞歸調(diào)用的死循環(huán)超埋,不過大家要記住搏讶,此方法是準(zhǔn)備和lowercaseString方法互換的佳鳖。所以,在運(yùn)行期媒惕,eoc_myLowercaseString選擇子實(shí)際上對(duì)應(yīng)于原有的lowercaseString方法實(shí)現(xiàn)系吩。最后,通過下列代碼來交換這兩個(gè)方法實(shí)現(xiàn):

Method class_getInstanceMethod(Class aClass,SEL aSelector);
    
Method originalMethod = class_getInstanceMethod([NSString class], @selector(lowercaseString));
Method swappedMethod = class_getInstanceMethod([NSString class], @selector(my_lowercaseString));
    
method_exchangeImplementations(originalMethod, swappedMethod);

執(zhí)行完上述代碼之后妒蔚,只要在NSString實(shí)例上調(diào)用lowercaseString方法穿挨,就會(huì)輸出一行記錄消息:

    NSString *string  = @"stRiNg";
    NSString *lowercaseString = [string lowercaseString];
// Output: stRiNg ==> string

通過此方案,開發(fā)者可以為那些“完全不知道其具體實(shí)現(xiàn)的”(completely opaque肴盏,“完全不透明的”)黑盒方法增加日志記錄功能絮蒿,這非常有助于程序調(diào)試。然而叁鉴,此做法只在調(diào)試程序時(shí)有用土涝。很少有人在調(diào)試程序之外的場合用上述“方法調(diào)配技術(shù)”來永久改動(dòng)某個(gè)類的功能。不能僅僅因?yàn)镺bjective-C語言里有這個(gè)特性就一定要用它幌墓。若是濫用但壮,反而會(huì)令代碼變得不易讀懂且難于維護(hù)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末常侣,一起剝皮案震驚了整個(gè)濱河市蜡饵,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌胳施,老刑警劉巖溯祸,帶你破解...
    沈念sama閱讀 211,376評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異舞肆,居然都是意外死亡焦辅,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門椿胯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來筷登,“玉大人,你說我怎么就攤上這事哩盲∏胺剑” “怎么了?”我有些...
    開封第一講書人閱讀 156,966評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵廉油,是天一觀的道長惠险。 經(jīng)常有香客問我,道長抒线,這世上最難降的妖魔是什么班巩? 我笑而不...
    開封第一講書人閱讀 56,432評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮十兢,結(jié)果婚禮上趣竣,老公的妹妹穿的比我還像新娘。我一直安慰自己旱物,他們只是感情好遥缕,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,519評(píng)論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著宵呛,像睡著了一般单匣。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上宝穗,一...
    開封第一講書人閱讀 49,792評(píng)論 1 290
  • 那天户秤,我揣著相機(jī)與錄音,去河邊找鬼逮矛。 笑死鸡号,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的须鼎。 我是一名探鬼主播鲸伴,決...
    沈念sama閱讀 38,933評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼晋控!你這毒婦竟也來了汞窗?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,701評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤赡译,失蹤者是張志新(化名)和其女友劉穎仲吏,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蝌焚,經(jīng)...
    沈念sama閱讀 44,143評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡裹唆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,488評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了只洒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片品腹。...
    茶點(diǎn)故事閱讀 38,626評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖红碑,靈堂內(nèi)的尸體忽然破棺而出舞吭,到底是詐尸還是另有隱情,我是刑警寧澤析珊,帶...
    沈念sama閱讀 34,292評(píng)論 4 329
  • 正文 年R本政府宣布羡鸥,位于F島的核電站,受9級(jí)特大地震影響忠寻,放射性物質(zhì)發(fā)生泄漏惧浴。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,896評(píng)論 3 313
  • 文/蒙蒙 一奕剃、第九天 我趴在偏房一處隱蔽的房頂上張望衷旅。 院中可真熱鬧捐腿,春花似錦、人聲如沸柿顶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽嘁锯。三九已至宪祥,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間家乘,已是汗流浹背蝗羊。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留仁锯,地道東北人耀找。 一個(gè)月前我還...
    沈念sama閱讀 46,324評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像业崖,于是被迫代替她去往敵國和親涯呻。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,494評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 下午接到一個(gè)有趣的問題: 看到這個(gè)問題的第一想法就是利用runtime的方法交換腻要,通過自己的方法替換系統(tǒng)方法复罐,在自...
    高浩浩浩浩浩浩閱讀 500評(píng)論 0 1
  • 前言 前段時(shí)間去面試的時(shí)候有一題問的是method swizzling是什么?請(qǐng)簡述原理以及如何使用雄家?oc的run...
    nuclear閱讀 584評(píng)論 0 5
  • 前言 通過OC這個(gè)“調(diào)方法” = “向?qū)ο蟀l(fā)消息”的機(jī)制效诅,我們了解到方法在類中都是一張類似于字典的表。select...
    蜂猴閱讀 193評(píng)論 0 0
  • 在OC中趟济,最具爭議的語法乱投,莫過于runtime中的運(yùn)行時(shí)的語法。而其中黑魔法Method Swizzling更讓人...
    mdiep閱讀 764評(píng)論 12 7
  • 作為一個(gè)開發(fā)人員, 有兩個(gè)詞無論是工作中還是面試中, 都會(huì)經(jīng)常聽見, 被問及:"進(jìn)程""線程"顷编。 在開始了解多線程...
    追夢赤子心Year閱讀 336評(píng)論 0 4