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博客。

目录
相关文章
|
1月前
|
开发框架 前端开发 Android开发
Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势
本文深入探讨了 Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势。这对于实现高效的跨平台移动应用开发具有重要指导意义。
160 4
|
1月前
|
开发框架 前端开发 Android开发
安卓与iOS开发中的跨平台策略
在移动应用开发的战场上,安卓和iOS两大阵营各据一方。随着技术的演进,跨平台开发框架成为开发者的新宠,旨在实现一次编码、多平台部署的梦想。本文将探讨跨平台开发的优势与挑战,并分享实用的开发技巧,帮助开发者在安卓和iOS的世界中游刃有余。
|
1月前
|
安全 数据安全/隐私保护 Android开发
探索Android与iOS的隐私保护策略
在数字时代,智能手机已成为我们生活中不可或缺的一部分,而随之而来的则是对个人隐私和数据安全的日益关注。本文将深入探讨Android与iOS两大操作系统在隐私保护方面的策略和实践,分析它们如何应对日益严峻的隐私挑战,以及用户应如何保护自己的数据安全。通过对比分析,我们将揭示两大系统在隐私保护方面的优势和不足,为用户提供有价值的见解和建议。
|
2月前
|
设计模式 安全 Swift
探索iOS开发:打造你的第一个天气应用
【9月更文挑战第36天】在这篇文章中,我们将一起踏上iOS开发的旅程,从零开始构建一个简单的天气应用。文章将通过通俗易懂的语言,引导你理解iOS开发的基本概念,掌握Swift语言的核心语法,并逐步实现一个具有实际功能的天气应用。我们将遵循“学中做,做中学”的原则,让理论知识和实践操作紧密结合,确保学习过程既高效又有趣。无论你是编程新手还是希望拓展技能的开发者,这篇文章都将为你打开一扇通往iOS开发世界的大门。
|
2月前
|
搜索推荐 IDE API
打造个性化天气应用:iOS开发之旅
【9月更文挑战第35天】在这篇文章中,我们将一起踏上iOS开发的旅程,通过创建一个个性化的天气应用来探索Swift编程语言的魅力和iOS平台的强大功能。无论你是编程新手还是希望扩展你的技能集,这个项目都将为你提供实战经验,帮助你理解从构思到实现一个应用的全过程。让我们开始吧,构建你自己的天气应用,探索更多可能!
77 1
|
3月前
Micronaut AOP与代理机制:实现应用功能增强,无需侵入式编程的秘诀
AOP(面向切面编程)能够帮助我们在不修改现有代码的前提下,为应用程序添加新的功能或行为。Micronaut框架中的AOP模块通过动态代理机制实现了这一目标。AOP将横切关注点(如日志记录、事务管理等)从业务逻辑中分离出来,提高模块化程度。在Micronaut中,带有特定注解的类会在启动时生成代理对象,在运行时拦截方法调用并执行额外逻辑。例如,可以通过创建切面类并在目标类上添加注解来记录方法调用信息,从而在不侵入原有代码的情况下增强应用功能,提高代码的可维护性和可扩展性。
82 1
|
20天前
|
开发框架 Android开发 iOS开发
安卓与iOS开发中的跨平台策略:一次编码,多平台部署
在移动应用开发的广阔天地中,安卓和iOS两大阵营各占一方。随着技术的发展,跨平台开发框架应运而生,它们承诺着“一次编码,到处运行”的便捷。本文将深入探讨跨平台开发的现状、挑战以及未来趋势,同时通过代码示例揭示跨平台工具的实际运用。
|
1月前
|
安全 Swift iOS开发
Swift 与 UIKit 在 iOS 应用界面开发中的关键技术和实践方法
本文深入探讨了 Swift 与 UIKit 在 iOS 应用界面开发中的关键技术和实践方法。Swift 以其简洁、高效和类型安全的特点,结合 UIKit 丰富的组件和功能,为开发者提供了强大的工具。文章从 Swift 的语法优势、类型安全、编程模型以及与 UIKit 的集成,到 UIKit 的主要组件和功能,再到构建界面的实践技巧和实际案例分析,全面介绍了如何利用这些技术创建高质量的用户界面。
33 2
|
1月前
|
前端开发 Android开发 开发者
探索Android与iOS的跨平台开发策略
在当今多元化的移动设备市场中,开发者面临着为不同操作系统设计应用的挑战。本文深入探讨了Android和iOS两大主流平台的跨平台开发策略。我们将分析使用Flutter、React Native等框架进行跨平台开发的优劣,并讨论如何克服各平台间的差异性,以实现高效、一致的用户体验。此外,文章还将提供一些实用的技巧和最佳实践,帮助开发者优化跨平台应用的性能和兼容性。
43 4
|
1月前
|
前端开发 Android开发 iOS开发
探索Android与iOS的跨平台开发策略
在移动应用开发的多元化时代,跨平台开发已成为开发者追求效率和广泛覆盖的重要手段。本文深入探讨了Android与iOS两大主流平台下的跨平台开发策略,分析了各自的优势与挑战,并通过实际案例展示了如何有效实施跨平台解决方案,以期为开发者提供有价值的参考和启示。