NSProxy实现AOP方便为ios应用实现异常处理策略

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

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

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

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

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

NSProxy:如其名,它出现的目的就是为了来“代理”一个真实对象的。这是Foundation库的内置实现。大部门人都知道NSObject是通常Cocoa中的根类,没错,但其实根类并不止一个,NSProxy也是跟NSObject的根类,只是它是个抽象类并且不是用于通常意义上的编程目的,所以不是那么广为人知(事实上我也是今天才知道)。并且NSObject看到它你以为它是个类。但今天看NSProxy定义的时候,我发现它的头文件里是这样定义的:

@interface NSProxy <NSObject>

开始我很莫名其妙,如果是继承自NSObject的话,应该是个冒号。这种写法明显就是实现协议的写法啊。于是,查看了一下资料,果然还有个NSObject的协议,并且NSObject类自身也实现了NSObject协议。具体资料请看这篇文章

NSProxy与NSObject一虚一实,并且它们都实现了NSObject协议。这让NSProxy的实现类能够很好地“代理”NSObject子类,并且把NSObject协议抽象出来,也让他们能够共享某些行为。

来看看它是如何工作的(测试代码见AOPLibTest.m文件):

在你需要使用AOP的地方,你首先需要实例化一个对象,比如你需要实例化一个NSMutableArray,你需要使用AOPProxy来实例化它:

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

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

- (id) initWithNewInstanceOfClass:(Class) class {

    // create a new instance of the specified class
    id newInstance = [[class alloc] init];

    // invoke my designated initializer
    [self initWithInstance:newInstance];

    // release the new instance
    [newInstance release];

    // finally return the configured self
    return self;
}

上面的self指代的就是AOPProxy,其中的initWithInstance方法:

- (id) initWithInstance:(id)anObject {

    parentObject = [anObject retain];

    methodStartInterceptors = [[NSMutableArray alloc] init];
    methodEndInterceptors = [[NSMutableArray alloc] init];

    return self;
}

可以看到,它在内部hold住了真实对象,并且实例化了两个数组,用来存储方法执行前后的拦截器集合。

下面,我们可以为NSMutableArray增加拦截器了:

[(AOPProxy*)testArray interceptMethodStartForSelector:@selector(addObject:)
                                    withInterceptorTarget:self
                                      interceptorSelector:@selector( addInterceptor: )];

    [(AOPProxy*)testArray interceptMethodEndForSelector:@selector(removeObjectAtIndex:)
                                  withInterceptorTarget:self
                                    interceptorSelector:@selector( removeInterceptor: )];

因为这两个方法是AOPProxy的实例方法,所以在编写的时候还是需要在强制转回来(其实你在XCode里跟踪的时候,这里的testArray一直都是APOProxy类型的对象,因为一开始他就是被AOPPorxy
allo出来的)。这两个方法的实现很简单,只是将拦截器假如相应的数组中去,待后面取出来执行。

[testArray addObject:[NSNumber numberWithInt:1]];

    [testArray removeObjectAtIndex:0];

好了,看起来这里开始调用某个对象本身的行为了。为什么说看起来呢?难道不是吗。当然不是,我在上面已经说过了,这里只是取名为testArray事实上它并不是NSMutableArray的实例,而是AOPProxy的实例。但为什么它还是可以调用addObject这个方法呢,因为它被强制转换为NSMutableArray类型了,编辑器能够接受这样的类型转换,也就是这是合法的。所以编辑器认为它就是NSMutableArray类型的对象了,所以是可以这么调用的,但后面你会看到。在运行时其实编译器知道了它不是真实的NSMutableArray类型(也就是说它无法响应addObject以及removeObjectAtIndex这两个方法),所以把它交给了另一个专门的方法来处理这些无法响应的消息:

- (void)forwardInvocation:(NSInvocation *)anInvocation;

这个方法其实是继承自NSPorxy,NSProxy对它的实现其实就是抛出个异常,子类需要重新实现它,把它消息传递给真实的对象。详细信息参考官方文档

来看看它的实现:

- (void)forwardInvocation:(NSInvocation *)anInvocation;
{
    SEL aSelector = [anInvocation selector];

    // check if the parent object responds to the selector ...
    if ( [parentObject respondsToSelector:aSelector] ) {

        [anInvocation setTarget:parentObject];

        //
        // Intercept the start of the method.
        //

        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

        for ( int i = 0; i < [methodStartInterceptors count]; i++ ) {

            // first search for this selector ...
            AOPInterceptorInfo *oneInfo = [methodStartInterceptors objectAtIndex:i];

            if ( [oneInfo interceptedSelector] == aSelector ) {

                // extract the interceptor info
                id target = [oneInfo interceptorTarget];
                SEL selector = [oneInfo interceptorSelector];

                // finally invoke the interceptor
                [(NSObject *) target performSelector:selector withObject:anInvocation];
            }
        }

        [pool release];

        //
        // Invoke the original method ...
        //

        [self invokeOriginalMethod:anInvocation];

        //
        // Intercept the ending of the method.
        //

        NSAutoreleasePool *pool2 = [[NSAutoreleasePool alloc] init];

        for ( int i = 0; i < [methodEndInterceptors count]; i++ ) {

            // first search for this selector ...
            AOPInterceptorInfo *oneInfo = [methodEndInterceptors objectAtIndex:i];

            if ( [oneInfo interceptedSelector] == aSelector ) {

                // extract the interceptor info
                id target = [oneInfo interceptorTarget];
                SEL selector = [oneInfo interceptorSelector];

                // finally invoke the interceptor
                [(NSObject *) target performSelector:selector withObject:anInvocation];
            }
        }

        [pool2 release];
    }
//    else {
//        [super forwardInvocation:invocation];
//    }
}

可以砍到这里让真实的对象调用了方法,并且干涉了对象的行为,在其前后加入了拦截器的执行操作。从而“优雅”地实现了AOP。

该库中,还提供了两个Aspect:

AOPMethodLoger-用于简单记录方法的日志;

AOPThreadInvoker-用于在一个单独的线程上执行方法;

之前在Java以及.net中已经很广泛地应用了AOP的实例了,常见的应用有做Log啊,异常捕获啊之类的。最近在做iOS的应用,其中也会牵扯到异常捕获的问题,特别是牵扯到数据库操作以及业务逻辑上的异常,总是写代码捕获块儿,费事还占面积。所以,我在里面又加了一个Aspect:AOPExcettionCatcher。很简单,就是在这里统一实现了异常捕获。

重新实现了invokeOriginalMethod方法:

- (void)invokeOriginalMethod:(NSInvocation *)anInvocation{
    NSLog(@"%@",@"entry into try block");
    @try {
        [super invokeOriginalMethod:anInvocation];
    }
    @catch (NSException *exception) {
        NSLog(@"%@",@"entry into catch block");
        NSLog(@"%@",[exception reason]);
    }
    @finally {
        NSLog(@"%@",@"entry into finally block");
    }
}

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

原文发布时间为:2012-12-23

本文作者:vinoYang

本文来自合作伙伴CSDN博客,了解相关信息可以关注CSDN博客。

时间: 2024-09-13 02:43:01

NSProxy实现AOP方便为ios应用实现异常处理策略的相关文章

程序报错-ios程序中异常处理问题

问题描述 ios程序中异常处理问题 [NSCFconstantString isResizable],这个异常要怎么解决,这是我用NSString类型给UIImage赋值时出现的错误 解决方案 http://www.cocoachina.com/ios/20141229/10787.html 解决方案二: 具体代码怎么写的, UIImage.image = UIImage("named": xxx) 解决方案三: http://www.cocoachina.com/ios/201412

从AOP框架学习iOS Runtime

最近在一个iOS项目中添加了一个AOP框架,并且根据项目需求做了一些重构,重构的过程中对AOP的实现方式也做了下学习和分析,感觉还是很有趣的,下面就给大家分享一下个人所得.     在正式讲解iOS中AOP实现原理之前,有些准备知识需要讲一下      准备知识          准备知识一:Method,SEL,IMP概念     SEL      先看一下SEL的概念,Objective-C在编译时,会依据每一个方法的名字.参数序列,生成一个唯一的整型标识(Int类型的地址),这个标识就是S

Dora.Interception: 一个为.NET Core度身定制的AOP框架

多年从事框架设计开发使我有了一种强迫症,那就是见不得一个应用里频繁地出现重复的代码.之前经常Review别人的代码,一看到这样的程序,我就会想如何将这些重复的代码写在一个地方,然后采用"注入"的方式将它们放到需要的程序中.我们知道AOP是解决这类问题最理想的方案.为此,我自己写了一个AOP框架,该框架被命名为Dora.Interception.Dora.Interception已经在GitHub上开源,如果有兴趣的朋友想下载源代码或者阅读相关文档,可以访问GitHub地址:https:

Spring AOP Example Tutorial

这是一篇翻译,原文:Spring AOP Example Tutorial – Aspect, Advice, Pointcut, JoinPoint, Annotations, XML Configuration Spring 框架发展出了两个核心概念:依赖注入 和面向切面编程(AOP).我们已经了解了 Spring 的依赖注入 是如何实现的,今天我们来看看面向切面编程的核心概念以及 Spring 框架是如何实现它的. AOP 概要 大多数的企业应用都会有一些共同的对横切的关注,横切是否适用于

DotNet 资源大全

原文:DotNet 资源大全 转自:http://blog.jobbole.com/96676/ API 框架 NancyFx:轻量.用于构建 HTTP 基础服务的非正式(low-ceremony)框架,基于.Net 及 Mono 平台. ASP.NET WebAPI:快捷创建 HTTP 服务的框架,可以广泛用于多种不同的客户端,包括浏览器和移动设备. ServiceStack :架构缜密.速度飞快.令人愉悦的 web 服务. Nelibur:Nelibur 是一个使用纯 WCF 构建的基于消息

DotNet 资源大全中文版

原文:DotNet 资源大全中文版 转自:https://github.com/jobbole/awesome-dotnet-cn 我想很多程序员应该记得 GitHub 上有一个 Awesome - XXX 系列的资源整理.awesome-dotnet 是由 quozd 发起和维护.内容包括:编译器.压缩.应用框架.应用模板.加密.数据库.反编译.IDE.日志.风格指南等. Awesome 系列虽然挺全,但基本只对收录的资源做了极为简要的介绍,如果有更详细的中文介绍,对相应开发者的帮助会更大.这

利用Spring框架改进J2EE编程

j2ee|编程 摘要 J2EE编程正在变得越来越复杂.J2EE已经发展为一个API.复杂化的编程和配置的复杂网络.为了应对这种复杂性,新的框架和方法不断涌现.这些框架高度依赖于一个称为IoC(Inversion of Control,反向控制)的概念.本文将探讨这种方法的一些特性和优点,因为这种方法与J2EE编程相关,而且可以使J2EE编程变得更轻松. 简介 马克·吐温的一句话常被引用:"--关于我死亡的报道是一种夸张."现在已经出现了很多关于.Net的流言,以及认为J2EE API的

Enterprise Library深入解析与灵活应用(8):通过WCF扩展实现与EHAB的集成[

Enterprise Library深入解析与灵活应用(8):通过WCF扩展实现与EHAB的集成[上篇] 在<WCF技术剖析(卷1)>的最后一章,我给出了一个具体的应用WCF的分布式应用实例,我把这个实例命名为PetShop.在这个例子中,我利用WCF的扩展实 现了一些设计.架构模式,比如AOP.IoC等.看过本书的读者,一定还记得我还 通过WCF扩展实现了于微软企业库(Enterprise Library)异常处理应用块 (Exception Handling Application Blo

如何编写没有Try/Catch的程序

一.异常处理不简单 个人觉得,异常处理对于程序员来说,尤其是对于那些初级.NET程序员来说,是最为熟悉的同时也是最难掌握的.说它熟悉,因为仅仅就是Try/Catch而已.说它难以掌握,很多开发人员却说不清楚Try/Catch应该置于何处?什么情况下需要对异常进行日志记录?什么情况下需要对异常进行封装?什么情况下需要对异常进行替换?对于捕获的异常,在什么情况下需要将其再次抛出?什么情况下则不需要.总之,异常处理没有我们想象的那么简单. 无论对于何种类型的应用,异常处理都是必不可少的.合理的异常处理