基情四射的日子
我總是對神秘的事物產(chǎn)生興趣恶座,自從知道了Objective-C的Runtime技能便脊,我便試著接近它蚂四,討好它,因為只有對這個技能了如指掌哪痰,我才能征服它遂赠,才能在修仙的道路上順利前進。
消息傳遞函數(shù): objc_msgSend
這個可以說是Runtime系統(tǒng)的根基晌杰,因為所有的OC方法調(diào)用最終都會轉(zhuǎn)換成objc_msgSend函數(shù)的調(diào)用跷睦。這么重要的秘密,沒想到只給丫喝了兩瓶啤的肋演,它就告訴了我抑诸±们伲看來酒量不行,難在江湖混吶蜕乡。
招式破解:[receiver message] ----> objc_msgSend(receiver, selector, arg1, arg2, ...) ? ? objc_msgSend在執(zhí)行的過程中奸绷,會悄悄的傳入接收對象和方法選擇器這兩個參數(shù)。
看了Runtime醉酒的樣子层玲,我心里嘀咕著:“就這貨号醉,怎么能有這么強大的力量?”果然不出我所料称簿,這貨有一枚神器叫編譯器扣癣,一直在默默的幫助著它。就這個消息傳遞憨降,也是因為編譯器為每一個類和對象構(gòu)建了各自的結(jié)構(gòu)父虑,才能使其順利施放技能。那我倒是要看看授药,這個類結(jié)構(gòu)到底是啥士嚎?
類結(jié)構(gòu)破解: ? ?
類結(jié)構(gòu)包含了一個指向父類的指針(isa)和一個類調(diào)度表。類調(diào)度表記錄了類中選擇器對應(yīng)的具體內(nèi)存地址悔叽,就像這樣:selector ----> address 莱衩。當調(diào)用一個方法,也就是給對象發(fā)送一個消息的時候娇澎,會根據(jù)isa找到類結(jié)構(gòu)笨蚁,并在調(diào)度表中查找匹配的selector;如果不能匹配趟庄,objc_msgSend就根據(jù)isa到父類類結(jié)構(gòu)里與調(diào)度表中的selector匹配括细,依此類推直到NSObject類。一旦匹配到了selector戚啥,就調(diào)用表中記錄的函數(shù)(address)奋单,否則將啟動消息轉(zhuǎn)發(fā)機制。這種在運行時選擇方法實現(xiàn)的過程稱為動態(tài)綁定猫十。
強大的Runtime必然會有很多的招式览濒,這招動態(tài)綁定,我給90分拖云。因為我覺著這樣靈活的招式贷笛,肯定會有它的弊端。首先在編譯期不能確定方法的位置宙项,這就給運行時造成了不可避免的麻煩昨忆,從而導(dǎo)致效率上的問題。哼哼~果然被我找到了弱點杉允。但是后來的一番話邑贴,差點迷惑了我。
效率提升招式之緩存:?
每個類都有單獨的緩存叔磷,包含了繼承的selector和類中定義的selector(只有在曾今調(diào)用過的方法才會進行緩存)拢驾。在匹配調(diào)度表之前,先檢查接收對象的類緩存(哈希散列算法)改基,如果緩存中有匹配的selector繁疤,就直接使用。這大大減少了運行時查詢selector所消耗的時間秕狰。
我差點就信了稠腊。后來仔細一想,如果我連續(xù)多次調(diào)用一個特定的方法鸣哀,那不就玩完了嗎架忌?哈哈哈,丫果然有弱點我衬,看我怎么支配你叹放。
我也算是武學(xué)奇才,一會兒就研究了一個招式:規(guī)避動態(tài)綁定機制挠羔。
首先我要想辦法得到一個方法的內(nèi)存地址井仰,然后就直接調(diào)用啊,這不就沒有查找selector的過程了嗎破加。至于怎么得到方法的地址俱恶,就要靠methodForSelector方法了。請看下面的招式玩耍:
void (*showTitle)(id, SEL, NSString *);
int i;
showTitle = (void(*)(id, SEL, NSString *))[target methodForSelector:@selector(showMessage:)];
for (i=0; i<1000; ++i) {
? ? showTitle(targetList[i], @selector(showMessage:), "邪魔退散");
}
這樣在重復(fù)多次調(diào)用一個特定的方法范舀,有顯著的效率提升合是。
學(xué)會了這招技能,我看著熟睡的Runtime尿背,心想:“總有一天端仰,在你遇到問題的時候,我會助你一臂之力的田藐±笊眨”
不會吧,我竟然對它沒有了敵意汽久『捉撸看來在修仙的道路上,我會多一位兄弟(jiyou)了景醇。
關(guān)注微信公眾號CodingArtist臀稚,可以第一時間得到文章更新通知!? ^_^