参考: 面向切面编程: React Native
https://github.com/steipete/Aspects 是一个基于 Method Swizzle 的 iOS 函数替换的第三方库, 他可以很好的实现勾取一个类或者一个对象的某个方法, 支持在方法执行前 (AspectPositionBefore)/ 执行后(AspectPositionAfter) 或替代原方法执行(AspectPositionInstead)
需要导入的头文件:
#import <Aspects/Aspects.h>
对外的两个重要接口声明如下:
第一个: HOOK 一个类的所有实例的指定方法
- /// 为一个指定的类的某个方法执行前 / 替换 / 后, 添加一段代码块. 对这个类的所有对象都会起作用.
- ///
- /// @param block 方法被添加钩子时, Aspectes 会拷贝方法的签名信息.
- /// 第一个参数将会是 `id<AspectInfo>`, 余下的参数是此被调用的方法的参数.
- /// 这些参数是可选的, 并将被用于传递给 block 代码块对应位置的参数.
- /// 你甚至使用一个没有任何参数或只有一个 `id<AspectInfo>` 参数的 block 代码块.
- ///
- /// @注意 不支持给静态方法添加钩子.
- /// @return 返回一个唯一值, 用于取消此钩子.
- + (id<AspectToken>)aspect_hookSelector:(SEL)selector
- withOptions:(AspectOptions)options
- usingBlock:(id)block
- error:(NSError **)error;
第二个: HOOK 一个类实例的指定方法
- /// 为一个指定的对象的某个方法执行前 / 替换 / 后, 添加一段代码块. 只作用于当前对象.
- - (id<AspectToken>)aspect_hookSelector:(SEL)selector
- withOptions:(AspectOptions)options
- usingBlock:(id)block
- error:(NSError **)error;
三个重要 ** 参数 ** 如下:
- // 1, 被 HOOK 的元类, 类或者实例
- @property (nonatomic, unsafe_unretained, readonly) id instance;
- // 2, 方法参数列表
- @property (nonatomic, strong, readonly) NSArray *arguments;
- // 3, 原来的方法
- @property (nonatomic, strong, readonly) NSInvocation *originalInvocation;
- // 执行原来的方法
- [originalInvocation invoke];
基本使用
- +(void)Aspect {
- // 在类 UIViewController 所有的实例执行 viewWillAppear: 方法完毕后做一些事情
- [UIViewController aspect_hookSelector:@selector(viewWillAppear:)
- withOptions:AspectPositionAfter
- usingBlock:^(id<AspectInfo> info) {
- NSString *className = NSStringFromClass([[info instance] class]);
- NSLog(@"%@", className);
- } error:NULL];
- // 在实例 myVc 执行 viewWillAppear: 方法完毕后做一些事情
- UIViewController* myVc = [[UIViewController alloc] init];
- [myVc aspect_hookSelector:@selector(viewWillAppear:)
- withOptions:AspectPositionAfter
- usingBlock:^(id<AspectInfo> info) {
- id instance = info.instance; // 调用的实例对象
- id invocation = info.originalInvocation; // 原始的方法
- id arguments = info.arguments; // 参数
- [invocation invoke]; // 原始的方法, 再次调用
- } error:NULL];
- // HOOK 类方法
- Class metalClass = objc_getMetaClass(NSStringFromClass(UIViewController.class).UTF8String);
- [metalClass aspect_hookSelector:@selector(ClassMethod)
- withOptions:AspectPositionAfter
- usingBlock:^(id<AspectInfo> info) {
- NSLog(@"%@", HOOK 类方法);
- } error:NULL];
- }
注意:
所有的调用, 都会是线程安全的. Aspects 使用了 Objective-C 的消息转发机会, 会有一定的性能消耗. 所以对于过于频繁的调用, 不建议使用 Aspects.Aspects 更适用于视图 / 控制器相关的等每秒调用不超过 1000 次的代码.
当应用于某个类时(使用类方法添加钩子), 不能同时 hook 父类和子类的同一个方法; 否则会引起循环调用问题. 但是, 当应用于某个类的示例时(使用实例方法添加钩子), 不受此限制.
使用 KVO 时, 最好在 aspect_hookSelector: 调用之后添加观察者; 否则可能会引起崩溃.
参考链接
iOS--- 防止 UIButton 重复点击的三种实现方式
Aspects- iOS 的 AOP 面向切面编程的库
Objc 黑科技 - Method Swizzle 的一些注意事项
Aspects- iOS 的 AOP 面向切面编程的库
来源: https://juejin.im/post/5c0fd6be6fb9a049a570c0ef