Objective-C caching and Method Swizzling

翻譯自http://kevin.sb.org/2006/11/16/objective-c-caching-and-method-swizzling/
本文鏈接:http://www.reibang.com/p/c15d4690568e
Objective-C使用方法緩存機制道逗,針對某個類中被一個或者多個對象反復(fù)調(diào)用的方法進行優(yōu)化。上述情況很常見献烦,舉例來說滓窍,如果你遍歷某個數(shù)組,對每個元素執(zhí)行相同的操作巩那,你會在一堆同一Class的不同實例上調(diào)用同一個方法吏夯。實際上-[NSEnumerator nextObject]方法本身也被緩存了。
方法緩存存在一個潛在問題的原因是即横,沒人知道它是怎么運作的噪生,也沒人知道方法緩存會不會對方法注入(Method Swizzling)造成影響。
然而东囚,簡單的來說答案是NO跺嗽。
Objective-C的方法緩存通過存儲在每一個struct objc_classcache屬性中的一個小型散列表來工作的(類型是struct objc_cache)。這個散列表包含最近使用過的Method對象的指針,這個指針與從struct objc_classstruct objc_method_list中找到的Method對象是相同的桨嫁。重要的一點是緩存的Method對象實際上可以是該類某一個父類的方法植兰。這就使得緩存十分有用,如果一個方法被緩存了瞧甩,方法分發(fā)系統(tǒng)就不必每次都在各個類的層級中尋找這個方法钉跷。
Method Swizzling不需要擔(dān)心方法緩存的原因是,緩存的Method對象跟每個類里struct objc_method_list中的是同一個肚逸。當(dāng)Method Swizzling修改了Method對象爷辙,緩存也同時修改了,所以任何之后的緩存命中都會指向這個修改后的Method朦促。
如果你因為某些原因想刷新類的緩存(我想不到為啥要這么做)膝晾,只需要在你的文件里添加:

void _objc_flush_caches(Class cls);

并調(diào)用它,傳入你想刷新緩存的類务冕。這個特殊的方法也會同時刷新該類所有父類的緩存血当。
Method Swizzling
如果你想在你的APP里面用Method Swizzling(or Input Manager, or SIMBL plugin, or……),我認為下面的實現(xiàn)是最佳實踐禀忆。

void PerformSwizzle(Class aClass, SEL orig_sel, SEL alt_sel, BOOL forInstance)
{
    // First, make sure the class isn't nil
    if (aClass != nil) {
        Method orig_method = nil, alt_method = nil;
        // Next, look for the methods
        if (forInstance) {
            orig_method = class_getInstanceMethod(aClass, orig_sel);
            alt_method = class_getInstanceMethod(aClass, alt_sel);
        } else {
            orig_method = class_getClassMethod(aClass, orig_sel);
            alt_method = class_getClassMethod(aClass, alt_sel);
        }
        // If both are found, swizzle them
        if ((orig_method != nil) && (alt_method != nil)) {
            IMP temp;
            temp = orig_method->method_imp;
            orig_method->method_imp = alt_method->method_imp;
            alt_method->method_imp = temp;
        } else {
#if DEBUG
            NSLog(@"PerformSwizzle Error: Original %@, Alternate %@",(orig_method == nil)?@" not found":@" found",(alt_method == nil)?@" not found":@" found");
#endif
        }
    } else {
#if DEBUG
        NSLog(@"PerformSwizzle Error: Class not found");
#endif
    }
}
void MethodSwizzle(Class aClass, SEL orig_sel, SEL alt_sel)
{
    PerformSwizzle(aClass, orig_sel, alt_sel, YES);
}
void ClassMethodSwizzle(Class aClass, SEL orig_sel, SEL alt_sel)
{
    PerformSwizzle(aClass, orig_sel, alt_sel, NO);
}

這段特殊的實現(xiàn)交換了兩個方法的method_imp指針臊旭,因此調(diào)用第一個方法會調(diào)用第二個方法的實現(xiàn),反之亦然箩退。優(yōu)雅的部分是离熏,你要想調(diào)用被修改方法的原實現(xiàn),只需要調(diào)用你的新方法名就好了戴涝。接下來修改自SafariSource的代碼滋戳,示范了這一點。

- (void) mySetString:(NSString *)string {
    NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];
    if ([defs boolForKey:SafariSourceEnabled]) {
        // do stuff here
    } else {
        [self mySetString:string];
    }
}

當(dāng)任何代碼試圖調(diào)用這個類的setString:方法啥刻,注入的方法mySetString:就會被調(diào)用奸鸯。由于兩個方法進行了調(diào)換,調(diào)用mySetString:實際會調(diào)用原來的setString:方法可帽。因此當(dāng)看到代碼看上去是遞歸調(diào)用時娄涩,實際是調(diào)用了原來的函數(shù)。
這個實現(xiàn)的最大瑕疵是映跟,如果你想替換一個實際在父類里的實現(xiàn)的方法時钝满,方法注入會影響父類的所有實例,而不是只影響子類申窘。有些可能的方法,比如動態(tài)添加一個新類孔轴,用它來充當(dāng)舊的類剃法,但現(xiàn)在還沒有人能實現(xiàn)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末路鹰,一起剝皮案震驚了整個濱河市贷洲,隨后出現(xiàn)的幾起案子收厨,更是在濱河造成了極大的恐慌,老刑警劉巖优构,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件诵叁,死亡現(xiàn)場離奇詭異,居然都是意外死亡钦椭,警方通過查閱死者的電腦和手機拧额,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來彪腔,“玉大人侥锦,你說我怎么就攤上這事〉抡酰” “怎么了恭垦?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長格嗅。 經(jīng)常有香客問我番挺,道長,這世上最難降的妖魔是什么屯掖? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任玄柏,我火速辦了婚禮,結(jié)果婚禮上懂扼,老公的妹妹穿的比我還像新娘禁荸。我一直安慰自己,他們只是感情好阀湿,可當(dāng)我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布赶熟。 她就那樣靜靜地躺著,像睡著了一般陷嘴。 火紅的嫁衣襯著肌膚如雪映砖。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天灾挨,我揣著相機與錄音邑退,去河邊找鬼。 笑死劳澄,一個胖子當(dāng)著我的面吹牛地技,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播秒拔,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼莫矗,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起作谚,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤三娩,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后妹懒,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體雀监,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年眨唬,在試婚紗的時候發(fā)現(xiàn)自己被綠了会前。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡单绑,死狀恐怖回官,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情搂橙,我是刑警寧澤歉提,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站区转,受9級特大地震影響苔巨,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜废离,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一侄泽、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蜻韭,春花似錦悼尾、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至俯画,卻和暖如春析桥,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背艰垂。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工泡仗, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人猜憎。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓娩怎,卻偏偏與公主長得像,于是被迫代替她去往敵國和親胰柑。 傳聞我的和親對象是個殘疾皇子峦树,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,916評論 2 344

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

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉辣辫,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,682評論 0 9
  • 我們常常會聽說 Objective-C 是一門動態(tài)語言,那么這個「動態(tài)」表現(xiàn)在哪呢魁巩?我想最主要的表現(xiàn)就是 Obje...
    Ethan_Struggle閱讀 2,176評論 0 7
  • 文中的實驗代碼我放在了這個項目中。 以下內(nèi)容是我通過整理[這篇博客] (http://yulingtianxia....
    茗涙閱讀 913評論 0 6
  • 本文詳細整理了 Cocoa 的 Runtime 系統(tǒng)的知識姐浮,它使得 Objective-C 如虎添翼谷遂,具備了靈活的...
    lylaut閱讀 792評論 0 4
  • 轉(zhuǎn)載:http://yulingtianxia.com/blog/2014/11/05/objective-c-r...
    F麥子閱讀 728評論 0 2