AOP 是 Java Spring 框架另一个重要的功能,AOP(Aspect Oriented Programming)意为:面向切面编程,它是一种在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想,本文用于学习这种编程思想,并给出 iOS 中的实现。
为什么需要 AOP
iOS 开发当中不可避免的要与UIViewController
打交道,一个项目当中可能有很多UIViewController
的子类,像下面这样:
// ViewControllerA
@interface ViewControllerA:UIViewController
@end
@implementation ViewControllerA
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
@end
// ViewControllerB
@interface ViewControllerB:UIViewController
@end
@implementation ViewControllerB
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
@end
如果需要在viewDidLoad
方法里进行一些统计,使用面向对象的思路,我们通常会这样做:
//添加一个统计类
@interface Reporter:NSObject
- (void)record:(NSString *)message;
@end
@implementation Reporter
- (void)record:(NSString *)message{
// do something
}
@end
@interface ViewControllerA:UIViewController
@end
@implementation ViewControllerA
- (void)viewDidLoad {
[super viewDidLoad];
//调用统计代码
Reporter *reporter = [Reporter new];
[reporter record:@"something..."];
}
@end
@interface ViewControllerB:UIViewController
@end
@implementation ViewControllerB
- (void)viewDidLoad {
[super viewDidLoad];
//调用统计代码
Reporter *reporter = [Reporter new];
[reporter record:@"something..."];
}
@end
这样其实已经产生了耦合,每个UIViewController
的viewDidLoad
方法都要写上Reporter
的代码,当需要记录的信息越来越多时就会变得很臃肿。
或许你会想,我建个这样的基类不就可以了吗?
@interface RootController:UIViewController
@end
@implementation RootController
- (void)viewDidLoad {
[super viewDidLoad];
Reporter *reporter = [Reporter new];
[reporter record:@"something..."];
}
@end
@interface ViewControllerA:RootController
@end
@implementation ViewControllerA
- (void)viewDidLoad {
[super viewDidLoad];
}
@end
@interface ViewControllerB:RootController
@end
@implementation ViewControllerB
- (void)viewDidLoad {
[super viewDidLoad];
}
@end
确实,理论上需要用 AOP 解决的问题,OOP 也可以解决,但这并非是高效的做法,这样做就需要多维护一个RootController
类,并且如果其他工程需要用到ViewControllerA
和ViewControllerB
,也不方便直接移植过去。
这个问题的本质在于如何增强对象的方法,OOP 可以解决,但 AOP 可以更加高效优雅的解决。
使用 AOP 增强对象的方法
AOP 思想是在在运行时,动态地将代码切入到类的指定方法、指定位置上,以此来增强对象的方法。
本文示例采用的是 OC 语言,这里使用 Aspects 库来解决这个问题:
[UIViewController aspect_hookSelector:@selector(viewDidLoad:) withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> aspectInfo, BOOL animated) {
Reporter *reporter = [Reporter new];
[reporter record:@"something..."];
} error:NULL];
@end
这里使用 AOP 切入了UIViewController
的viewDidLoad
方法后,补充调用了Reporter
,轻松实现了解耦。
Aspects 库是基于runtime
实现的,提供了更完善的切入时机:
typedef NS_OPTIONS(NSUInteger, AspectOptions) {
AspectPositionAfter = 0, /// Called after the original implementation (default)
AspectPositionInstead = 1, /// Will replace the original implementation.
AspectPositionBefore = 2, /// Called before the original implementation.
AspectOptionAutomaticRemoval = 1 << 3 /// Will remove the hook after the first execution.
};
AOP 的用途
AOP 减少了重复代码和耦合,结构更加清晰,在这些场景使用会带来不错的效果:
- 日志输出
- 事件统计
- 权限检查
需要注意的是,AOP 拦截了所有的事件,使用不当可能会带一些意想不到的结果。
如果有需求要用到 AOP,最好选择一个成熟完善的框架,框架内部通常会提供较完善的异常处理和测试,可以帮助我们写出更完善和安全的代码。
iOS 开发强烈推荐使用 Aspects,具体使用和注意事项参考文档即可。
文档信息
- 本文作者:Ning Zhang
- 本文链接:https://sunsetroads.github.io/2020/01/15/aop/
- 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)