1. AOP簡介
AOP: Aspect Oriented Programming 面向切面編程。
面向切面編程(也叫面向方面):Aspect Oriented Programming(AOP),是目前軟件開發(fā)中的一個熱點。利用AOP可以對業(yè)務(wù)邏輯的各個部分進行隔離收恢,從而使得業(yè)務(wù)邏輯各部分之間的耦合度降低擎颖,提高程序的可重用性,同時提高了開發(fā)的效率购裙。
AOP是OOP的延續(xù),是(Aspect Oriented Programming)的縮寫,意思是面向切面(方面)編程坯台。
主要的功能是:日志記錄,性能統(tǒng)計瘫寝,安全控制蜒蕾,事務(wù)處理,異常處理等等焕阿。
主要的意圖是:將日志記錄咪啡,性能統(tǒng)計,安全控制暮屡,事務(wù)處理撤摸,異常處理等代碼從業(yè)務(wù)邏輯代碼中劃分出來,通過對這些行為的分離褒纲,我們希望可以將它們獨立到非指導(dǎo)業(yè)務(wù)邏輯的方法中准夷,進而改? 變這些行為的時候不影響業(yè)務(wù)邏輯的代碼。
可以通過預(yù)編譯方式和運行期動態(tài)代理實現(xiàn)在不修改源代碼的情況下給程序動態(tài)統(tǒng)一添加功能的一種技術(shù)莺掠。AOP實際是GoF設(shè)計模式的延續(xù)衫嵌,設(shè)計模式孜孜不倦追求的是調(diào)用者和被調(diào)用者之間的解耦,AOP可以說也是這種目標的一種實現(xiàn)彻秆。
假設(shè)把應(yīng)用程序想成一個立體結(jié)構(gòu)的話楔绞,OOP的利刃是縱向切入系統(tǒng)论悴,把系統(tǒng)劃分為很多個模塊(如:用戶模塊,文章模塊等等)墓律,而AOP的利刃是橫向切入系統(tǒng)膀估,提取各個模塊可能都要重復(fù)操作的部分(如:權(quán)限檢查,日志記錄等等)耻讽。由此可見察纯,AOP是OOP的一個有效補充。
注意:AOP不是一種技術(shù)针肥,實際上是編程思想饼记。凡是符合AOP思想的技術(shù),都可以看成是AOP的實現(xiàn)
2. iOS中的AOP
利用 Objective-C 的 Runtime 特性慰枕,我們可以給語言做擴展具则,幫助解決項目開發(fā)中的一些設(shè)計和技術(shù)問題。這一篇具帮,我們來探索一些利用 Objective-C Runtime 的黑色技巧博肋。這些技巧中最具爭議的或許就是 Method Swizzling 。
其次蜂厅,用不用就看項目規(guī)模和團隊規(guī)模匪凡。有些業(yè)務(wù)確實非常適合使用AOP,比如log,AOP還可以用來debug
AOP的優(yōu)勢:
減少切面業(yè)務(wù)的開發(fā)量掘猿,“一次開發(fā)終生使用”病游,比如日志
減少代碼耦合,方便復(fù)用稠通。切面業(yè)務(wù)的代碼可以獨立出來衬衬,方便其他應(yīng)用使用
提高代碼review的質(zhì)量,比如我可以規(guī)定某些類的某些方法才用特定的命名規(guī)范改橘,這樣review的時候就可以發(fā)現(xiàn)一些問題
AOP的弊端:
它破壞了代碼的干凈整潔滋尉。
(因為 Logging 的代碼本身并不屬于 ViewController 里的主要邏輯。隨著項目擴大唧龄、代碼量增加兼砖,你的 ViewController 里會到處散布著 Logging 的代碼。這時既棺,要找到一段事件記錄的代碼會變得困難讽挟,也很容易忘記添加事件記錄的代碼)
3. iOS AOP實戰(zhàn)
玩轉(zhuǎn) Method Swizzling
1.事務(wù)攔截,安全可變?nèi)萜?/p>
iOS中有各類容器的概念丸冕,容器分可變?nèi)萜骱头强勺內(nèi)萜鞯⒚罚勺內(nèi)萜饕话銉?nèi)部在實現(xiàn)上是一個鏈表,在進行各類(insert 胖烛、remove眼姐、 delete诅迷、 update )難免有空操作、指針越界的問題众旗。
最粗暴的方式就是在使用可變?nèi)萜鞯臅r間罢杉,每次操作都必須手動做空判斷、索引比較這些操作
NSMutableDictionary *dic = [[NSMutableDictionary alloc] init];
if (obj) {
[dic setObject:obj forKey:@"key"];
}
NSMutableArray *array = [[NSMutableArray alloc] init];
if (index < array.count) {
NSLog(@"%@",[array objectAtIndex:index]);
}
在代碼中大量的使用這鞋操作實在是太過于繁瑣了贡歧,試想如果可變?nèi)萜髯陨砣绾文茏鲞@些兼容豈不是更好滩租。可能會想到繼承的方法來解決利朵,但是項目中盡可能的避免過多的派生(至于派生的弊端這里就不多說了)律想;或者想到分類,這里也不盡人意绍弟。
Method Swizzling 移花接木
runtime 這里就不多多說了(swift里面已經(jīng)對這個概念的說法從心轉(zhuǎn)變成了 Reflection<反射>)技即,objective c中每個方法的名字(SEL)跟函數(shù)的實現(xiàn)(IMP)是一一對應(yīng)的,Swizzle的原理只是在這個地方做下手腳樟遣,將原來方法名與實現(xiàn)的指向交叉處理而叼,就能達到一個新的效果。
廢話少說年碘,直接上代碼:
這里使用NSMutableArray 做實例澈歉,為NSMutableArray追加一個新的方法
@implementation NSMutableArray (safe)
+ (void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
id obj = [[self alloc] init];
[obj swizzleMethod:@selector(addObject:) withMethod:@selector(safeAddObject:)];
[obj swizzleMethod:@selector(objectAtIndex:) withMethod:@selector(safeObjectAtIndex:)];
[obj swizzleMethod:@selector(insertObject:atIndex:) withMethod:@selector(safeInsertObject:atIndex:)];
[obj swizzleMethod:@selector(removeObjectAtIndex:) withMethod:@selector(safeRemoveObjectAtIndex:)];
[obj swizzleMethod:@selector(replaceObjectAtIndex:withObject:) withMethod:@selector(safeReplaceObjectAtIndex:withObject:)];
});
}
- (void)safeAddObject:(id)anObject
{
if (anObject) {
[self safeAddObject:anObject];
}else{
NSLog(@"obj is nil");
}
}
- (id)safeObjectAtIndex:(NSInteger)index
{
if(index<[self count]){
return [self safeObjectAtIndex:index];
}else{
NSLog(@"index is beyond bounds ");
}
return nil;
}
- (void)swizzleMethod:(SEL)origSelector withMethod:(SEL)newSelector
{
Class class = [self class];
Method originalMethod = class_getInstanceMethod(class, origSelector);
Method swizzledMethod = class_getInstanceMethod(class, newSelector);
BOOL didAddMethod = class_addMethod(class,
origSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
class_replaceMethod(class,
newSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}
這里唯一可能需要解釋的是 class_addMethod 展鸡。要先嘗試添加原 selector 是為了做一層保護屿衅,因為如果這個類沒有實現(xiàn) originalSelector ,但其父類實現(xiàn)了莹弊,那 class_getInstanceMethod 會返回父類的方法涤久。這樣 method_exchangeImplementations 替換的是父類的那個方法,這當然不是你想要的忍弛。所以我們先嘗試添加 orginalSelector 响迂,如果已經(jīng)存在,再用 method_exchangeImplementations 把原方法的實現(xiàn)跟新的方法實現(xiàn)給交換掉细疚。
safeAddObject 代碼看起來可能有點奇怪蔗彤,像遞歸不是么。當然不會是遞歸疯兼,因為在 runtime 的時候然遏,函數(shù)實現(xiàn)已經(jīng)被交換了。調(diào)用 objectAtIndex: 會調(diào)用你實現(xiàn)的 safeObjectAtIndex:吧彪,而在 NSMutableArray: 里調(diào)用 safeObjectAtIndex: 實際上調(diào)用的是原來的 objectAtIndex: 待侵。
如此以來,一直擔心的問題就迎刃而解了姨裸,不僅在可變數(shù)組秧倾、可變字典等容器內(nèi)都可以做自己想做的事情怨酝。