Objective-C 下的 AOP 编程

来源:互联网 发布:1万左右的耳机 知乎 编辑:程序博客网 时间:2024/05/22 11:55

Objective-C 下的 AOP 编程

概念

在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,是函数式编程的一种衍生范型。通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。主要的功能是:日志记录,性能统计,安全控制,事务处理,异常处理等等。主要的意图是:将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。

运用

这里举个例子,我们有个方法sumA:andB:, 用来返回ab之和的一个字串,我们在这个方法前和方法后都增加个一段代码

  • 在运行方法前我们把参数改成2和3, 当然这里是演示用,实际用的时候别改参数,不然其他同事真的要骂人了
  • 在运行方法后我们输出传入的参数和返回值
[objc] view plaincopy在CODE上查看代码片派生到我的代码片
  1. - (void)clickTestAop:(id)sender  
  2. {  
  3.     AopTestM *test = [[AopTestM alloc] init];  
  4.     NSLog(@"run1");  
  5.     [test sumA:1 andB:2];  
  6.       
  7.     NSString *before = [XYAOP interceptClass:[AopTestM class] beforeExecutingSelector:@selector(sumA:andB:) usingBlock:^(NSInvocation *invocation) {  
  8.         int a = 3;  
  9.         int b = 4;  
  10.           
  11.         [invocation setArgument:&a atIndex:2];  
  12.         [invocation setArgument:&b atIndex:3];  
  13.           
  14.         NSLog(@"berore fun. a = %d, b = %d", a , b);  
  15.     }];  
  16.       
  17.     NSString *after =  [XYAOP interceptClass:[AopTestM class] afterExecutingSelector:@selector(sumA:andB:) usingBlock:^(NSInvocation *invocation) {  
  18.         int a;  
  19.         int b;  
  20.         NSString *str;  
  21.           
  22.         [invocation getArgument:&a atIndex:2];  
  23.         [invocation getArgument:&b atIndex:3];  
  24.         [invocation getReturnValue:&str];  
  25.           
  26.         NSLog(@"after fun. a = %d, b = %d, sum = %@", a , b, str);  
  27.     }];  
  28.       
  29.     NSLog(@"run2");  
  30.     [test sumA:1 andB:2];  
  31.       
  32.     [XYAOP removeInterceptorWithIdentifier:before];  
  33.     [XYAOP removeInterceptorWithIdentifier:after];  
  34.       
  35.     NSLog(@"run3");  
  36.     [test sumA:1 andB:2];  
  37. }   
  38.   
  39. - (NSString *)sumA:(int)a andB:(int)b  
  40. {  
  41.     int value = a + b;  
  42.     NSString *str = [NSString stringWithFormat:@"fun running. sum : %d", value];  
  43.     NSLog(@"%@", str);  
  44.       
  45.     return str;  
  46. }  


我们执行这段代码的时候,大伙猜猜结果是啥.结果如下

[objc] view plaincopy在CODE上查看代码片派生到我的代码片
  1. 2014-10-28 22:52:47.215 JoinShow[3751:79389] run1  
  2. 2014-10-28 22:52:52.744 JoinShow[3751:79389] fun running. sum : 3  
  3. 2014-10-28 22:52:52.745 JoinShow[3751:79389] run2  
  4. 2014-10-28 22:52:52.745 JoinShow[3751:79389] berore fun. a = 3, b = 4  
  5. 2014-10-28 22:52:52.745 JoinShow[3751:79389] fun running. sum : 7  
  6. 2014-10-28 22:52:52.745 JoinShow[3751:79389] after fun. a = 3, b = 4, sum = fun running. sum : 7  
  7. 2014-10-28 22:52:52.746 JoinShow[3751:79389] run3  
  8. 2014-10-28 22:52:52.746 JoinShow[3751:79389] fun running. sum : 3  

实现原理

用Objective-C强大的runtime.

我们知道当给一个对象发送一个方法的时候, 如果当前类和父类都没实现该方法的时候就会走转发流程

  • 动态方法解析 -> 快速消息转发 -> 标准消息转发

迷茫的同学请搜 "Objective-C 消息转发".

了解了消息转发,那么我们aop的思路就来了,我们是先干掉原本的方法funa,这样当给对象发送方法的时候就会走转发流程,我们再hook了对象的快速消息转发方法,把实现funa的对象指成我们的aop对象, 最后在aop对象的标准消息转发里执行before instead after方法.

具体的代码欢迎大伙去github下载, 记得给咱点个star

link https://github.com/uxyheaven/XYQuickDevelop

在代码里搜 XYAOP.h

相关一些方法介绍

介绍一些用到的runtime方法

[objc] view plaincopy在CODE上查看代码片派生到我的代码片
  1. // 给 cls 添加一个新方法  
  2. BOOL class_addMethod (  
  3.    Class cls,  
  4.    SEL name,  
  5.    IMP imp,  
  6.    const charchar *types  
  7. );  
  8.   
  9. // 替换 cls 里的一个方法的实现  
  10. IMP class_replaceMethod (  
  11.    Class cls,  
  12.    SEL name,  
  13.    IMP imp,  
  14.    const charchar *types  
  15. );  
  16.   
  17. // 返回 cls 的指定方法  
  18. Method class_getInstanceMethod (  
  19.    Class cls,  
  20.    SEL name  
  21. );  
  22.   
  23. // 设置一个方法的实现  
  24. IMP method_setImplementation (  
  25.    Method m,  
  26.    IMP imp  
  27. );  
  28.   
  29. // 返回 cls 里的 name 方法的实现  
  30. IMP class_getMethodImplementation (  
  31.    Class cls,  
  32.    SEL name  
  33. );  
0 0