Aop切面编程
1 什么是切面编程
2 KVO就是一个切面编程的例子
3 借鉴KVO切面编程,用自己的方法实现
4 代码实现
切面编程概念
这种在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。
KVO切面编程实例
原理:Apple使用了isa 混写(isa - swizzling)来实现。如图
OC基础-KVO
关于KVO详细内容请看我的另一篇文章https://www.jianshu.com/p/c98c995bda08
实现自己的Aop
先了解一下OC消息转发的大致原理。
1 动态添加 resolveInstanceMethod:
2 重定向 forwardingTargetForSelector:
3 方法签名后,调用forwardingTargetForSelector
第3步,灵活性是最大的,可以改变方法的参数内容,个数,方法名字,方法接收对象等。Aop切面编程就是用的第三个。
假设Person类,有一个实例方法sayHello(); 现在要求,每次调用sayHello()之前调用一段代码Before,sayHello之后调用一段代码after。
1 创建子类,避免代码入侵。
动态创建一个Person类的子类FWHook_Person ,改变person对象的isa指向FWHook_Person. 后面的步奏,都是针对FWHook_Person的
2 把sayHello的方法实现保存在 方法alias_sayHello中
3 替换sayHello方法实现为_objc_msgForward。使得person调用sayHello方法时,立即触发消息转发机制。
4 替换forwardInvocation:为FWHook_forwardInvocation。在这里我们实现,我们需要做的一些具体操作
Aop流程图
当[person sayHello]时,实际调用[person _objc_msgForward], 触发消息转发调用 forwardInvocation的替换方法FWHook_forwardInvocation,在FWHook_forwardInvocation中依次调用before、alias_sayHello、after.
代码实现
1 FWHookItem对block进行代码签名
@interface FWHookItem : NSObject
@property (nonatomic, weak) id object;
@property (nonatomic, assign) SEL selector;
@property (nonatomic, assign) FWHookOption option;
@property (nonatomic, strong) id block;
@property (nonatomic, strong) NSMethodSignature *blockSignature;
+(instancetype)itemWithObject:(id)object selector:(SEL)selector option:(FWHookOption)option block:(id)block;
/*
方法调用
*/
-(void)invokeWithParams:(NSArray*)params;
@end
2 FWHookContainer保存FWHookItem
@interface FWHookContainer : NSObject
@property(nonatomic,readonly)NSMutableArray<FWHookItem*> *beforeArray;
@property(nonatomic,readonly)NSMutableArray<FWHookItem*> *insteadArray;
@property(nonatomic,readonly)NSMutableArray<FWHookItem*> *afterArray;
-(void)addHookItem:(FWHookItem*)item;
@end
3 获取selector参数
@interface NSInvocation (FWArguments)
- (NSArray *)getParams;
@end
4 使用
typedef void (^ParamsBlock)(NSArray *params);
@interface NSObject (FWHook)
/*
selector : 需要监听的方法
option : 决定 block 执行时机,在selector的之前,之后,替代。
block :执行的代码块 返回的params就是,selector中的参数
*/
-(void)hookSelector:(SEL)selector withOption:(FWHookOption)option usingBlock:(ParamsBlock)block;
+(void)hookSelector:(SEL)selector withOption:(FWHookOption)option usingBlock:(ParamsBlock)block;
@end