Aspects是一个简洁高效的用于使iOS支持AOP面向切面编程的库.它可以帮助你在不改变一个类或类实例的代码的前提下,有效更改类的行为.
AOP (面向切面编程):
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
在以下场景中,用AOP技术可以节省很多的工作量:
1.用户通过客户端获取服务器端数据,做权限检查。
2.用户和市场交互,为用户的操作提供相应地购买参考或相关商品。
3.需要做日志记录的操作。
安装:
使用Aspects的项目要求是在ARC环境下,系统要求是iOS 7或更高,和OS X 10.7 及更高版本。满足环境要求后,就可以使用Cocopods进行安装了。
在Podfile下添加:
pod 'AspectsV1.4.2', '~> 1.4.2’
然后,命令行执行:
pod install --verbose —no-repo-update
执行成功后,重新打开我们的项目目录下.xcworkspace文件。
主要方法:
Aspects为NSObject类提供了如下的方法:
+ (id<AspectToken>)aspect_hookSelector:(SEL)selector
withOptions:(AspectOptions)options
usingBlock:(id)block
error:(NSError **)error;
- (id<AspectToken>)aspect_hookSelector:(SEL)selector
withOptions:(AspectOptions)options
usingBlock:(id)block
error:(NSError **)error;
这两个方法,都可以用来修改原方法。
返回值是一个id<AspectToken>对象,可以用来注销在方法中所做的修改:
id<AspectToken> aspect = ...;
[aspect remove];
参数里面,第一个selector用来设置需要添加功能的方法,options有如下选择:
AspectPositionAfter = 0, ///在原方法调用完成以后进行调用
AspectPositionInstead = 1, ///取代原方法 AspectPositionBefore = 2, ///在原方法调用签执行
AspectOptionAutomaticRemoval = 1 << 3 ///在调用了一次后清除(只能在对象方法中使用)
block是用来写功能模块,参数可以为空,默认的第一个参数是id<AspectInfo>类型。AspectInfo协议有如下方法:
/// 对象调用时,放回对象
- (id)instance;
/// 方法的原始实现
- (NSInvocation *)originalInvocation;
/// 原方法调用的参数
- (NSArray *)arguments;
最后error用于返回一个错误的指针。
官方的一个实用例子:
[PSPDFDrawView aspect_hookSelector:@selector(shouldProcessTouches:withEvent:) withOptions:AspectPositionInstead usingBlock:^(id<AspectInfo> info, NSSet *touches, UIEvent *event) {
// Call original implementation.
BOOL processTouches;
NSInvocation *invocation = info.originalInvocation;
[invocation invoke];
[invocation getReturnValue:&processTouches];
if (processTouches) {
processTouches = pspdf_stylusShouldProcessTouches(touches, event);
[invocation setReturnValue:&processTouches];
}
} error:NULL];
使用:
我们创建好一个工程,用pods安装好Aspects。
接着添加一个UIViewController的子类TwoViewController,从当前的ViewController点击button可以present到TwoViewController,然后点击TwoViewController又可以dismiss到ViewController;再给ViewController实现两个touch方法,分别为点击开始和点击结束,用它们来改变ViewController的view属性的一些可见的属性,最后添加一个私有方法setupUISystem,也让它完成一些改变view属性的功能,在viewDidLoad调用。
好了,基本功能完成后,运行我们的工程,看是否按照预期执行。
然后在AppDelegate的- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 生命周期方法中加入Aspects的代码:
[UIViewController aspect_hookSelector:@selector(viewWillAppear:) withOptions:AspectPositionAfter usingBlock:^(){
NSLog(@"view appear! -->>>>>>>>>><<<<<<<<<<<<<--");
} error:nil];
[ViewController aspect_hookSelector:@selector(touchesBegan:withEvent:) withOptions:AspectPositionInstead usingBlock:^() {
NSLog(@"color orange!");
}error:nil];
[ViewController aspect_hookSelector:@selector(touchesEnded:withEvent:) withOptions:AspectPositionAfter usingBlock:^(){
NSLog(@"color purle");
} error:nil];
ViewController *vc = [[ViewController alloc] init];
[vc aspect_hookSelector:@selector(setupUISystem) withOptions:AspectPositionAfter|AspectOptionAutomaticRemoval usingBlock:^(){
NSLog(@"setupUISystem As");
} error:nil];
再运行,看看控制台的是否有输出,原工程的功能是否有所改变。
我们发现:
1.在父类中的修改的方法,对子类都产生的作用。(1.4.2的特性)
2.Aspects的方法都按照预期执行,修改了原功能。
3.私有方法也可以被修改。
其他:
Aspectes 会自动标记自己,所有很容易在调用栈中查看某个方法是否已经调用:
最后附上一个统计功能的库:https://github.com/orta/ARAnalytics
更多优质文章请关注微信公众号(iOS优文)