ios開發(fā)Runtime詳解part3(Method swizzling)

??在 ios開發(fā) Runtime 詳解part1ios開發(fā) Runtime 詳解part2(動(dòng)態(tài)方法解析)中我大致介紹了runtime的基本功能妹卿,在這篇文章里,重點(diǎn)介紹一下runtime的一個(gè)重要的功能---method swizzling缩搅。
??說到method swizzling,不得不介紹一下AOP(Aspect Oriented Programming),即面向切面編程。 AOP在java開發(fā)中因?yàn)橛兄粋€(gè)牛逼的框架spring的存在使得AOP能夠得以發(fā)揚(yáng)光大娜膘,那么在ios開發(fā)中,AOP有哪些作用呢优质?下面我來大致列舉一下:
1竣贪、記錄日志,這也是用的最多的一種巩螃。
2演怎、事務(wù)管理,如數(shù)據(jù)庫(kù)的提交避乏。
3爷耀、處理緩存。
4拍皮、安全檢查歹叮,如權(quán)限管理。
??由于漢字的博大精深铆帽,切面兩個(gè)字已經(jīng)將這一思想做了很好的詮釋咆耿,但是如果沒有深入的體會(huì)還是很難理解的。我們知道爹橱,OOP(面向?qū)ο?是把一切操作都針對(duì)對(duì)象進(jìn)行操作萨螺,而面向切面則是對(duì)切面進(jìn)行的操作,也就是對(duì)業(yè)務(wù)的某一個(gè)層面進(jìn)行的操作。
??好比我們要對(duì)所有的網(wǎng)絡(luò)請(qǐng)求做一個(gè)日志功能慰技,大家首先想到的辦法肯定是在網(wǎng)絡(luò)請(qǐng)求的代碼里面加上日志請(qǐng)求的代碼椭盏,但是假設(shè)這個(gè)網(wǎng)絡(luò)請(qǐng)求的代碼是被封裝起來的,我們沒有辦法去改變這個(gè)請(qǐng)求的源代碼惹盼,這時(shí)候就可以用method swizzling來用我們自定義的方法來替換原有的網(wǎng)絡(luò)請(qǐng)求的方法庸汗,在里面加上日志請(qǐng)求的代碼,同時(shí)也能夠執(zhí)行網(wǎng)絡(luò)請(qǐng)求代碼手报。也就是在既有的業(yè)務(wù)層面中插入新的切面蚯舱,來處理通用的功能。

那么掩蛤,method swizzling怎么寫呢枉昏?
先上代碼:

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class class = [self class];
        
        SEL originalSelector = @selector(viewWillAppear:);
        SEL swizzledSelector = @selector(xxx_viewWillAppear:);
        
        Method originalMethod = class_getInstanceMethod(class, originalSelector);
        Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
        
        // 如果要替換class method,用下面的方法:
        // Class class = object_getClass((id)self);
        // ...
        // Method originalMethod = class_getClassMethod(class, originalSelector);
        // Method swizzledMethod = class_getClassMethod(class, swizzledSelector);
        
        BOOL didAddMethod =
        class_addMethod(class,
                        originalSelector,
                        method_getImplementation(swizzledMethod),
                        method_getTypeEncoding(swizzledMethod));
        
        if (didAddMethod) {
            class_replaceMethod(class,
                                swizzledSelector,
                                method_getImplementation(originalMethod),
                                method_getTypeEncoding(originalMethod));
        } else {
            method_exchangeImplementations(originalMethod, swizzledMethod);
        }
        
    });
}

#pragma mark - Method Swizzling
- (void)xxx_viewWillAppear:(BOOL)animated {
    [self xxx_viewWillAppear:animated];
    NSLog(@"viewWillAppear: %@", self);
}

??使用method swizzling是存在隱患的揍鸟,一旦使用不當(dāng)會(huì)帶來很大麻煩, 下面就列舉一下隱患以及避免的辦法兄裂,如果你不是很熟悉method swizzling,不要輕易在項(xiàng)目中使用阳藻!

1晰奖、 method swizzling是非原子性的,也就是說method swizzling是非線程安全的腥泥。

??我們知道匾南,+ load方法是在類被初始化的時(shí)候調(diào)用,+ initialize是程序第一次調(diào)用方法前調(diào)用的蛔外,如果我們把method swizzling寫在+ initialize方法里蛆楞, 就可能出現(xiàn)原方法可能在方法替換之前執(zhí)行,所以所有的method swizzling都應(yīng)該寫在+ load方法里夹厌。

2豹爹、method swizzling可能會(huì)改變?cè)瓉眍愔械拇a

??使用method swizzling是為了改變?cè)械姆椒ǎ绻阒粸榱艘粋€(gè)地方替換了方法矛纹,可這時(shí)所有用到這個(gè)方法的地方都受到了改變臂聋,帶來的危害是無法估量的。所以我們?cè)谔鎿Q的方法里一定要調(diào)用替換的方法(如上面例子中的[self xxx_viewWillAppear:animated])或南,因?yàn)樘鎿Q的方法已經(jīng)替換了原有的實(shí)現(xiàn)逻住,所以不是遞歸調(diào)用,如果繼續(xù)調(diào)用原生的實(shí)現(xiàn)則會(huì)出現(xiàn)遞歸循環(huán)迎献。

3、method swizzling可能會(huì)造成方法名沖突

??想象一下腻贰,如果你在類中用method swizzling中替換了一個(gè)方法吁恍,又在category中又?jǐn)U展了這個(gè)方法,這時(shí)候就會(huì)出現(xiàn)方法名沖突,通過給替換的方法設(shè)一個(gè)指針可以解決這個(gè)問題:

typedef IMP *IMPPointer;

BOOL class_swizzleMethodAndStore(Class class, SEL original, IMP replacement, IMPPointer store) {
    IMP imp = NULL;
    Method method = class_getInstanceMethod(class, original);
    if (method) {
        const char *type = method_getTypeEncoding(method);
        imp = class_replaceMethod(class, original, replacement, type);
        if (!imp) {
            imp = method_getImplementation(method);
        }
    }
    if (imp && store) { *store = imp; }
    return (imp != NULL);
}
// 在NSObject的category中可以添加一個(gè)通用方法來做安全的method swizzling
@implementation NSObject (FRRuntimeAdditions)
+ (BOOL)swizzle:(SEL)original with:(IMP)replacement store:(IMPPointer)store {
    return class_swizzleMethodAndStore(self, original, replacement, store);
}
@end
4冀瓦、使用method swizzling時(shí)在傳遞參數(shù)時(shí)可能改變參數(shù)的值伴奥,使用上面的方法可以解決。
5翼闽、方法替換的順序問題

??想象一下拾徙,我們按UIButton、UIControl感局、UIView的順序在它們之中替換了三個(gè)setFrame方法尼啡,和我們按UIView、UIControl询微、UIButton這個(gè)順序替換三個(gè)setFrame方法的結(jié)果是不一樣的崖瞭。那么怎么保證這個(gè)順序問題呢?
??如果我們只是要在載入類中使用撑毛,只要在load中進(jìn)行method swizzling就可以了书聚,因?yàn)閟uper class的load總是在子類的load之前執(zhí)行,從而保證了順序問題藻雌。

6雌续、不容易debug

??寫好文檔、寫好文檔胯杭、寫好文檔驯杜,重要的事情說三遍,method swizzling一定要寫好文檔歉摧,否則就像埋下了一個(gè)一個(gè)地雷艇肴,后患無窮。
??總結(jié):method swizzling可以方便的增加切面叁温,用很少的代碼就可以實(shí)現(xiàn)aop再悼,使用的過程中一定要注意可能遇到的問題,使用得當(dāng)必定是開發(fā)中的一把利器膝但。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末冲九,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子跟束,更是在濱河造成了極大的恐慌莺奸,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,997評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件冀宴,死亡現(xiàn)場(chǎng)離奇詭異灭贷,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)略贮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門甚疟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來仗岖,“玉大人,你說我怎么就攤上這事览妖≡簦” “怎么了?”我有些...
    開封第一講書人閱讀 163,359評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵讽膏,是天一觀的道長(zhǎng)檩电。 經(jīng)常有香客問我,道長(zhǎng)府树,這世上最難降的妖魔是什么俐末? 我笑而不...
    開封第一講書人閱讀 58,309評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮挺尾,結(jié)果婚禮上鹅搪,老公的妹妹穿的比我還像新娘。我一直安慰自己遭铺,他們只是感情好丽柿,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,346評(píng)論 6 390
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著魂挂,像睡著了一般甫题。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上涂召,一...
    開封第一講書人閱讀 51,258評(píng)論 1 300
  • 那天坠非,我揣著相機(jī)與錄音,去河邊找鬼果正。 笑死炎码,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的秋泳。 我是一名探鬼主播潦闲,決...
    沈念sama閱讀 40,122評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼迫皱!你這毒婦竟也來了歉闰?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,970評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤卓起,失蹤者是張志新(化名)和其女友劉穎和敬,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體戏阅,經(jīng)...
    沈念sama閱讀 45,403評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡昼弟,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,596評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了奕筐。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片舱痘。...
    茶點(diǎn)故事閱讀 39,769評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蚕键,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出衰粹,到底是詐尸還是另有隱情,我是刑警寧澤笆怠,帶...
    沈念sama閱讀 35,464評(píng)論 5 344
  • 正文 年R本政府宣布铝耻,位于F島的核電站,受9級(jí)特大地震影響蹬刷,放射性物質(zhì)發(fā)生泄漏瓢捉。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,075評(píng)論 3 327
  • 文/蒙蒙 一办成、第九天 我趴在偏房一處隱蔽的房頂上張望泡态。 院中可真熱鬧,春花似錦迂卢、人聲如沸某弦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,705評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽靶壮。三九已至,卻和暖如春员萍,著一層夾襖步出監(jiān)牢的瞬間腾降,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,848評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工碎绎, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留螃壤,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,831評(píng)論 2 370
  • 正文 我出身青樓筋帖,卻偏偏與公主長(zhǎng)得像奸晴,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子幕随,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,678評(píng)論 2 354

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