[iOS] XAspect学习总结
本文是在学习** XAspect**的简单笔记, 没有多少深的见解, 老司机请直接绕行,谢谢! 主要是看着英文文档记录的, 水平有限, 不对的地方, 还请指正...
Aspect是一个轻量级的面向切面编程的库。它能允许你在每一个类和每一个实例中存在的方法里面加入任何代码。
首先, 给出github地址: XAspect, 原版介绍, 可以看项目中的README, 或者详细的介绍Documents或者这个文章Aspects iOS的AOP面向切面编程的库.如果你对其源码感兴趣, 可以参考这篇文章 iOS 如何实现Aspect Oriented Programming (上)
下载源码后, 把** XAspect文件夹拖入项目, 或者如果使用Pod, 添加以下代码到你的Podfile**文件:
pod "Aspects"
然后, 新建一个空的** .m**文件:
这里的名称可以随便起, 建议使用功能相关的名称.
接着,打开这个文件, 添加如下代码:
#import "XAspect.h"
#import "AppDelegate.h"
// 必须要有的宏定义, 而且必须写在最前面
// AtAspect字段固定, 不能修改
// LZProjectName 这个可以自定义, 一般和自己需要注入代码的类有关
#define AtAspect LZProjectName
// 需要注入代码的类
// AtAspectOfClass 固定字段, 不能修改
// AppDelegate 需要注入代码的类
#define AtAspectOfClass AppDelegate
// 开辟空间, 这个参数类名需和上面的类一致
@classPatchField(AppDelegate)
/**
需要添加的方法
@param - 方法类型: - 实例方法; + 类方法
@param BOOL 返回值类型
@param UIApplication 方法实体
@return 返回
*/
AspectPatch(-, BOOL, application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions) {
NSLog(@"app did launched");
return XAMessageForward(application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions);
}
@end
#undef AtAspectOfClass
#undef AtAspect
这里是往AppDelegate类的application:(UIApplication )application didFinishLaunchingWithOptions:(NSDictionary )launchOptions方法内注入代码, 只是一个输出语句;
你可以在这里写任何需要在程序启动的时候初始化的代码, 例如: 分享, 推送等第三方信息注册.
其效果和写在AppDelegate类的application:(UIApplication )application didFinishLaunchingWithOptions:(NSDictionary )launchOptions是一样的.
同样, 他也可以为任意系统的类的方法注入代码, 例如:
#define AtAspectOfClass NSObject
@classPatchField(NSObject)
/**
需要添加的方法
@param - 方法类型: - 实例方法; + 类方法
@param BOOL 返回值类型
@param UIApplication 方法实体
@return 返回
*/
AspectPatch(-, instancetype, init)
{
// Add your custom implementation here.
NSLog(@"[Init]: %@", NSStringFromClass([self class]));
// Forward the message to the origin implementation.
return XAMessageForward(init);
}
@end
这是在所有NSObject的类初始化的时候都会打印这个信息.
再给出一个:
#define AtAspectOfClass UIViewController
@classPatchField(UIViewController)
@synthesizeNucleusPatch(Default, -, void, viewDidLoad);
@synthesizeNucleusPatch(Default, -, void, viewDidAppear:(BOOL)animated);
AspectPatch(-, void, viewDidLoad){
DDLogInfo(@"[CocoaLumberjack Log]: %@'s view did load.", NSStringFromClass([self class]));
return XAMessageForward(viewDidLoad);
}
AspectPatch(-, void, viewDidAppear:(BOOL)animated){
DDLogInfo(@"[CocoaLumberjack Log]: %@'s view did appear.", NSStringFromClass([self class]));
return XAMessageForward(viewDidAppear:(BOOL)animated);
}
@end
以上是基本的使用, 下面来看一下用到的语句:
#define AtAspect LZProjectName
定义一个名称空间, 这个宏会在当前的上下文生成一个唯一的标示符, 使用这个宏定义一个名称空间是强制要求的
#define AtAspectOfClass AppDelegate
@classPatchField(AppDelegate)
@end
使用@classPatchField(<#ClassName#>) ... @end字段来创建一个类来实现你需要注入的代码, 其功能类似于@ implementation……@end. 在这之前, 你需要使用宏#define AtAspectOfClass <#ClassName#>类定义需要注入代码的类, 这里的类名和@classPatchField(<#ClassName#>)中的参数类名要一致.
@synthesizeNucleusPatch(Default, -, void, viewDidLoad);
在** XAspect实现方法之前, 必须保证源类中存在这个方法. 宏@synthesizeNucleusPatch()**可以检测源类是否存在这个方法, 如果不存在, 回去尝试调用默认的一个方法, 否则会抛出异常 .
参数:
- 第一个: 类型, 有两种
1.Default : 如果当前类及其父类都没有实现这个方法, XAspect会尝试调用默认的一个返回值为nil/0的方法;
2.SuperCaller : 如果其父类实现了这个方法, 而当前类没有实现, XAspect会去调用父类的方法; - 第二个: 方法类型 - 实例方法; + 类方法
- 第三个: 方法返回值类型
- 第四个: 方法实体
@tryCustomizeDefaultPatch()
如果我们想当某个方法不存的时候去指定一个返回值类型, 可以使用这个方法,
例如:如果源类没有实现-application:didFinishLaunchingWithOptions:方法, 我们希望直接返回YES, 可以这么写:
@tryCustomizeDefaultPatch(1, -, BOOL, application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions) {
// 不要在这里实现任何逻辑, 只需要返回一个值;
return YES;
}
如果, 你想改变父类方法时的返回值或者行为, 你可以这么做:
@tryCustomizeSupercallerPatch(1, -, void, viewDidLoad) {
XAMessageForwardSuper(viewDidLoad); // invoke superclass's implementation.
}
XAMessageForward(application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions)
在AspectPatch宏中, 需要使用** XAMessageForward**宏将消息转发给源类.
写的比较乱, 更多的可以参考源码里的demo.