[轉]NSProxy實現AOP方便爲ios應用實現異常處理策略

[轉載自:http://blog.csdn.net/yanghua_kobe/article/details/8395535html

 

  前段時間關注過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 }  

  固然了這只是應用之一,你還能夠用它作更多的事情。

相關文章
相關標籤/搜索