[轉載自:http://blog.csdn.net/yanghua_kobe/article/details/8395535]html
前段時間關注過objc實現的AOP,在GitHub找到了其中的兩個庫:AOP-in-Objective-C 和 AOP-for-Objective-C。第一個是基於NSProxy來實現的;第二個是基於GCD以及block實現的。二者都使用了Cocoa的運行時編程技術,將攔截器注入給代理對象,使其干涉真是對象的執行順序從而達到給代碼增長「切面」的目的,這裏的模式就是一般的代理模式。git
由於時間關係,暫時只看了第一個庫的代碼,下面簡短地分析一下。github
NSProxy:如其名,它出現的目的就是爲了來「代理」一個真實對象的。這是Foundation庫的內置實現。大部門人都知道NSObject是一般Cocoa中的根類,沒錯,但其實根類並不止一個,NSProxy也是跟NSObject的根類,只是它是個抽象類而且不是用於一般意義上的編程目的,因此不是那麼廣爲人知(事實上我也是今天才知道)。而且NSObject看到它你覺得它是個類。但今天看NSProxy定義的時候,我發現它的頭文件裏是這樣定義的:數據庫
1 @interface NSProxy <NSObject>
開始我很莫名其妙,若是是繼承自NSObject的話,應該是個冒號。這種寫法明顯就是實現協議的寫法啊。因而,查看了一下資料,果真還有個NSObject的協議,而且NSObject類自身也實現了NSObject協議。具體資料請看這篇文章。編程
NSProxy與NSObject一虛一實,而且它們都實現了NSObject協議。這讓NSProxy的實現類可以很好地「代理」NSObject子類,而且把NSObject協議抽象出來,也讓他們可以共享某些行爲。來看看它是如何工做的(測試代碼見AOPLibTest.m文件):數組
在你須要使用AOP的地方,你首先須要實例化一個對象,好比你須要實例化一個NSMutableArray,你須要使用AOPProxy來實例化它:app
1 NSMutableArray* testArray = (NSMutableArray*)[[AOPProxy alloc] initWithNewInstanceOfClass:[NSMutableArray class]];
這裏,實際上是間接實例化。它提供了一個接口,你把你的類名傳給它,由它給你實例化。事實上,這是一種注入方式,而看完這個方法的定義你就會看到其實它返回給你的並非NSMutableArray的一個實例(實際上是AOPProxy,而它們之因此能互相強制轉換是由於他們都實現了NSObject協議):iphone
1 - (id) initWithNewInstanceOfClass:(Class) class { 2 3 // create a new instance of the specified class 4 id newInstance = [[class alloc] init]; 5 6 // invoke my designated initializer 7 [self initWithInstance:newInstance]; 8 9 // release the new instance 10 [newInstance release]; 11 12 // finally return the configured self 13 return self; 14 }
上面的self指代的就是AOPProxy,其中的initWithInstance方法:編輯器
1 - (id) initWithInstance:(id)anObject { 2 3 parentObject = [anObject retain]; 4 5 methodStartInterceptors = [[NSMutableArray alloc] init]; 6 methodEndInterceptors = [[NSMutableArray alloc] init]; 7 8 return self; 9 }
能夠看到,它在內部hold住了真實對象,而且實例化了兩個數組,用來存儲方法執行先後的攔截器集合。測試
下面,咱們能夠爲NSMutableArray增長攔截器了:
1 [(AOPProxy*)testArray interceptMethodStartForSelector:@selector(addObject:) 2 withInterceptorTarget:self 3 interceptorSelector:@selector( addInterceptor: )]; 4 5 [(AOPProxy*)testArray interceptMethodEndForSelector:@selector(removeObjectAtIndex:) 6 withInterceptorTarget:self 7 interceptorSelector:@selector( removeInterceptor: )];
由於這兩個方法是AOPProxy的實例方法,因此在編寫的時候仍是須要在強制轉回來(其實你在XCode裏跟蹤的時候,這裏的testArray一直都是APOProxy類型的對象,由於一開始他就是被AOPPorxy allo出來的)。這兩個方法的實現很簡單,只是將攔截器假如相應的數組中去,待後面取出來執行。
1 [testArray addObject:[NSNumber numberWithInt:1]]; 2 [testArray removeObjectAtIndex:0];
好了,看起來這裏開始調用某個對象自己的行爲了。爲何說看起來呢?難道不是嗎。固然不是,我在上面已經說過了,這裏只是取名爲testArray事實上它並非NSMutableArray的實例,而是AOPProxy的實例。但爲何它仍是能夠調用addObject這個方法呢,由於它被強制轉換爲NSMutableArray類型了,編輯器可以接受這樣的類型轉換,也就是這是合法的。因此編輯器認爲它就是NSMutableArray類型的對象了,因此是能夠這麼調用的,但後面你會看到。在運行時其實編譯器知道了它不是真實的NSMutableArray類型(也就是說它沒法響應addObject以及removeObjectAtIndex這兩個方法),因此把它交給了另外一個專門的方法來處理這些沒法響應的消息:
-(void)forwardInvocation:(NSInvocation*)anInvocation;
這個方法實際上是繼承自NSPorxy,NSProxy對它的實現其實就是拋出個異常,子類須要從新實現它,把它消息傳遞給真實的對象。詳細信息參考官方文檔!
來看看它的實現:
1 - (void)forwardInvocation:(NSInvocation *)anInvocation; 2 { 3 SEL aSelector = [anInvocation selector]; 4 5 // check if the parent object responds to the selector ... 6 if ( [parentObject respondsToSelector:aSelector] ) { 7 8 [anInvocation setTarget:parentObject]; 9 10 // 11 // Intercept the start of the method. 12 // 13 14 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 15 16 for ( int i = 0; i < [methodStartInterceptors count]; i++ ) { 17 18 // first search for this selector ... 19 AOPInterceptorInfo *oneInfo = [methodStartInterceptors objectAtIndex:i]; 20 21 if ( [oneInfo interceptedSelector] == aSelector ) { 22 23 // extract the interceptor info 24 id target = [oneInfo interceptorTarget]; 25 SEL selector = [oneInfo interceptorSelector]; 26 27 // finally invoke the interceptor 28 [(NSObject *) target performSelector:selector withObject:anInvocation]; 29 } 30 } 31 32 [pool release]; 33 34 // 35 // Invoke the original method ... 36 // 37 38 [self invokeOriginalMethod:anInvocation]; 39 40 41 // 42 // Intercept the ending of the method. 43 // 44 45 NSAutoreleasePool *pool2 = [[NSAutoreleasePool alloc] init]; 46 47 for ( int i = 0; i < [methodEndInterceptors count]; i++ ) { 48 49 // first search for this selector ... 50 AOPInterceptorInfo *oneInfo = [methodEndInterceptors objectAtIndex:i]; 51 52 if ( [oneInfo interceptedSelector] == aSelector ) { 53 54 // extract the interceptor info 55 id target = [oneInfo interceptorTarget]; 56 SEL selector = [oneInfo interceptorSelector]; 57 58 // finally invoke the interceptor 59 [(NSObject *) target performSelector:selector withObject:anInvocation]; 60 } 61 } 62 63 [pool2 release]; 64 } 65 // else { 66 // [super forwardInvocation:invocation]; 67 // } 68 }
能夠看到這裏讓真實的對象調用了方法,而且干涉了對象的行爲,在其先後加入了攔截器的執行操做。從而「優雅」地實現了AOP。
該庫中,還提供了兩個Aspect:
AOPMethodLoger-用於簡單記錄方法的日誌;
AOPThreadInvoker-用於在一個單獨的線程上執行方法;
以前在Java以及.net中已經很普遍地應用了AOP的實例了,常見的應用有作Log啊,異常捕獲啊之類的。最近在作iOS的應用,其中也會牽扯到異常捕獲的問題,特別是牽扯到數據庫操做以及業務邏輯上的異常,老是寫代碼捕獲塊兒,費事還佔面積。因此,我在裏面又加了一個Aspect:AOPExcettionCatcher。很簡單,就是在這裏統一實現了異常捕獲。
從新實現了invokeOriginalMethod方法:
1 - (void)invokeOriginalMethod:(NSInvocation *)anInvocation{ 2 NSLog(@"%@",@"entry into try block"); 3 @try { 4 [super invokeOriginalMethod:anInvocation]; 5 } 6 @catch (NSException *exception) { 7 NSLog(@"%@",@"entry into catch block"); 8 NSLog(@"%@",[exception reason]); 9 } 10 @finally { 11 NSLog(@"%@",@"entry into finally block"); 12 } 13 }
固然了這只是應用之一,你還能夠用它作更多的事情。