??在 ios開發(fā) Runtime 詳解part1和 ios開發(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ā)中的一把利器膝但。