前段时间关注过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. // create a new instance of the specified class
  3. id newInstance = [[class alloc] init];
  4. // invoke my designated initializer
  5. [self initWithInstance:newInstance];
  6. // release the new instance
  7. [newInstance release];
  8. // finally return the configured self
  9. return self;
  10. }

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

  1. - (id) initWithInstance:(id)anObject {
  2. parentObject = [anObject retain];
  3. methodStartInterceptors = [[NSMutableArray alloc] init];
  4. methodEndInterceptors = [[NSMutableArray alloc] init];
  5. return self;
  6. }

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

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

  1. [(AOPProxy*)testArray interceptMethodStartForSelector:@selector(addObject:)
  2. withInterceptorTarget:self
  3. interceptorSelector:@selector( addInterceptor: )];
  4. [(AOPProxy*)testArray interceptMethodEndForSelector:@selector(removeObjectAtIndex:)
  5. withInterceptorTarget:self
  6. 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. // check if the parent object responds to the selector ...
  5. if ( [parentObject respondsToSelector:aSelector] ) {
  6. [anInvocation setTarget:parentObject];
  7. //
  8. // Intercept the start of the method.
  9. //
  10. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  11. for ( int i = 0; i < [methodStartInterceptors count]; i++ ) {
  12. // first search for this selector ...
  13. AOPInterceptorInfo *oneInfo = [methodStartInterceptors objectAtIndex:i];
  14. if ( [oneInfo interceptedSelector] == aSelector ) {
  15. // extract the interceptor info
  16. id target = [oneInfo interceptorTarget];
  17. SEL selector = [oneInfo interceptorSelector];
  18. // finally invoke the interceptor
  19. [(NSObject *) target performSelector:selector withObject:anInvocation];
  20. }
  21. }
  22. [pool release];
  23. //
  24. // Invoke the original method ...
  25. //
  26. [self invokeOriginalMethod:anInvocation];
  27. //
  28. // Intercept the ending of the method.
  29. //
  30. NSAutoreleasePool *pool2 = [[NSAutoreleasePool alloc] init];
  31. for ( int i = 0; i < [methodEndInterceptors count]; i++ ) {
  32. // first search for this selector ...
  33. AOPInterceptorInfo *oneInfo = [methodEndInterceptors objectAtIndex:i];
  34. if ( [oneInfo interceptedSelector] == aSelector ) {
  35. // extract the interceptor info
  36. id target = [oneInfo interceptorTarget];
  37. SEL selector = [oneInfo interceptorSelector];
  38. // finally invoke the interceptor
  39. [(NSObject *) target performSelector:selector withObject:anInvocation];
  40. }
  41. }
  42. [pool2 release];
  43. }
  44. //    else {
  45. //        [super forwardInvocation:invocation];
  46. //    }
  47. }

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

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

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

  1. [转]NSProxy实现AOP方便为ios应用实现异常处理策略

    [转载自:http://blog.csdn.net/yanghua_kobe/article/details/8395535] 前段时间关注过objc实现的AOP,在GitHub找到了其中的两个库:A ...

  2. 关于ios “<null>”的异常处理

    在iOS开发过程中经常需要与服务器进行数据通讯,但是在数据接通过程中会出现:null "<null>"等问题导致莫名其妙的崩溃. 相信你一定会写各种判断来处理这些异常, ...

  3. IOS开发之--异常处理--使用try 和 catch 来捕获错误。

    一个搞java的老板问我会不会try catch  我说不会 学这么久也没听周围朋友用这个 因为苹果控制台本来就可以打印异常 特此研究一下. 1.try catch:  是捕获异常代码段   特点:对 ...

  4. ios资源加载策略

    做了好几个月的ios,大框架都是别人搭好的,自己只是实现逻辑,很是失落.慢慢开始整理学习一些概念类的东西吧,希望自己能提高点. cocos2d-x从cocos2d-2.0-x-2.0.2开始,考虑到自 ...

  5. iOS原生数据存储策略

    一 @interface NSCache : NSObject Description A mutable collection you use to temporarily store transi ...

  6. IOS之Objective-C学习 策略模式

    对于策略模式,我个人理解策略模式就是对各种规则的一种封装的方法,而不仅仅是对算法的封装与调用而已.与工厂模式中简单工厂有点类似,但是比简单工厂更有耦合度,因为策略模式以相同的方法调用所有的规则,减少了 ...

  7. IOS开发中缓存策略

    为了节约流量,同时也是为了更好的用户体验,目前很多应用都使用本地缓存机制,其中以网易新闻的缓存功能最为出色.我自己的应用也想加入本地缓存的功能,于是我从网上查阅了相关的资料,发现总体上说有两种方法.一 ...

  8. IOS开发之----异常处理

    本文转载至 http://blog.csdn.net/chenyong05314/article/details/7906593 转载自:http://blog.sina.com.cn/s/blog_ ...

  9. iOS开发系列-异常处理

    概述 在开发中经常调用苹果的API遇到数组越界.实例方法不存在运行时等致命错误,此时程序直接奔溃.其实苹果是在函数内部抛出了一个异常.这样告诉开发者需要检查代码做修改.同样在我们自己封装一些框架或者功 ...

随机推荐

  1. 我常用的前端开发工具—cutterman,mark man,sublime text,yeoman,gulp……

    虽然才刚刚开始练习切图,不过之前还是接触到不少工具的,决定一一用上,果然用了一天就切完了一个psd,对于一个菜鸟来说,还是很开心的. 我先从学ui网下载了一个psd.切图肯定是要用的ps的啦,这里和大 ...

  2. Flask 启动配置

    数据迁移.

  3. Notepad++-第一篇命令行语句执行之编译、运行Java

    1.让Notepad++编译和运行Java,在电脑上要已经配置好了Java的开发环境 2.在Notepad++上面的选项栏中找到 Plugins--->Plugin Admin 3.在Avail ...

  4. 牛客小白月赛16 A 小石的签到题 ( 博弈)

    链接:https://ac.nowcoder.com/acm/contest/949/A来源:牛客网 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 262144K,其他语言52428 ...

  5. Codeforces 609E (Kruskal求最小生成树+树上倍增求LCA)

    题面 传送门 题目大意: 给定一个无向连通带权图G,对于每条边(u,v,w)" role="presentation" style="position: rel ...

  6. 十二、支持向量机(Support Vector Machines)

    12.1 优化目标 参考视频: 12 - 1 - Optimization Objective (15 min).mkv 到目前为止,你已经见过一系列不同的学习算法.在监督学习中,许多学习算法的性能都 ...

  7. B.Petr and a Combination Lock

    https://codeforces.com/contest/1097/problem/A Petr and a Combination Lock time limit per test 1 seco ...

  8. Failed to determine the https port for redirect

    原文:Failed to determine the https port for redirect warn: Microsoft.AspNetCore.HttpsPolicy.HttpsRedir ...

  9. 利用JFreeChart生成简单柱状图(Java)

    package barchartdemo1; import <a href="http://lib.csdn.net/base/javaee" class='replace_ ...

  10. Linux性能优化从入门到实战:16 文件系统篇:总结磁盘I/O指标/工具、问题定位和调优

    (1)磁盘 I/O 性能指标 文件系统和磁盘 I/O 指标对应的工具 文件系统和磁盘 I/O 工具对应的指标 (2)磁盘 I/O 问题定位分析思路 (3)I/O 性能优化思路 Step 1:首先采用 ...