zoukankan      html  css  js  c++  java
  • NSProxy实现AOP方便为ios应用实现异常处理策略

    前段时间关注过objc实现的AOP

    在GitHub找到了其中的两个库:AOP-in-Objective-C 和 AOP-for-Objective-C

    第一个是基于NSProxy来实现的;第二个是基于GCD以及block实现的;

    两者都使用了Cocoa的运行时编程技术,将拦截器注入给代理对象,使其干涉真是对象的执行顺序从而达到给代码增加“切面”的目的,这里的模式就是通常的代理模式。

    因为时间关系,暂时只看了第一个库的代码,下面简短地分析一下。

    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来实例化它:

    1. NSMutableArray* testArray = (NSMutableArray*)[[AOPProxy alloc] initWithNewInstanceOfClass:[NSMutableArray class]];  

    这里,其实是间接实例化。它提供了一个接口,你把你的类名传给它,由它给你实例化。事实上,这是一种注入方式,而看完这个方法的定义你就会看到其实它返回给你的并不是NSMutableArray的一个实例(其实是AOPProxy,而它们之所以能互相强制转换是因为他们都实现了NSObject协议):

    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.       
    3.     [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. }  

    当然了这只是应用之一,你还可以用它做更多的事情。


  • 相关阅读:
    h5页面 禁止缩放
    Mac 建立自己的服务器,支持PHP
    socket(孔、插座 --> 套接字) Socket通信 -- 了解
    核心动画
    popoverPresentationController UIPopoverController 使用方法详解(气泡控制器)
    plist中的中文数据
    Attempt to present <vc> on <vc> which is already presenting <vc>/(null)
    Xcode 8 : iOS xib is missing from working copy、iOS misssing file
    Xcode 中的黄色文件夹/蓝色文件夹
    iOS 容器 addChildViewController
  • 原文地址:https://www.cnblogs.com/zsw-1993/p/4879384.html
Copyright © 2011-2022 走看看