1 什么是切面編程
2 KVO就是一個(gè)切面編程的例子
3 借鑒KVO切面編程,用自己的方法實(shí)現(xiàn)
4 代碼實(shí)現(xiàn)
切面編程概念
這種在運(yùn)行時(shí),動(dòng)態(tài)地將代碼切入到類的指定方法、指定位置上的編程思想就是面向切面的編程。
KVO切面編程實(shí)例
原理:Apple使用了isa 混寫(isa - swizzling)來(lái)實(shí)現(xiàn)。如圖

關(guān)于KVO詳細(xì)內(nèi)容請(qǐng)看我的另一篇文章http://www.itdecent.cn/p/c98c995bda08
實(shí)現(xiàn)自己的Aop
先了解一下OC消息轉(zhuǎn)發(fā)的大致原理。
1 動(dòng)態(tài)添加 resolveInstanceMethod:
2 重定向 forwardingTargetForSelector:
3 方法簽名后,調(diào)用forwardingTargetForSelector
第3步,靈活性是最大的,可以改變方法的參數(shù)內(nèi)容,個(gè)數(shù),方法名字,方法接收對(duì)象等。Aop切面編程就是用的第三個(gè)。
假設(shè)Person類,有一個(gè)實(shí)例方法sayHello(); 現(xiàn)在要求,每次調(diào)用sayHello()之前調(diào)用一段代碼Before,sayHello之后調(diào)用一段代碼after。
1 創(chuàng)建子類,避免代碼入侵。
動(dòng)態(tài)創(chuàng)建一個(gè)Person類的子類FWHook_Person ,改變person對(duì)象的isa指向FWHook_Person. 后面的步奏,都是針對(duì)FWHook_Person的
2 把sayHello的方法實(shí)現(xiàn)保存在 方法alias_sayHello中
3 替換sayHello方法實(shí)現(xiàn)為_objc_msgForward。使得person調(diào)用sayHello方法時(shí),立即觸發(fā)消息轉(zhuǎn)發(fā)機(jī)制。
4 替換forwardInvocation:為FWHook_forwardInvocation。在這里我們實(shí)現(xiàn),我們需要做的一些具體操作

當(dāng)[person sayHello]時(shí),實(shí)際調(diào)用[person _objc_msgForward], 觸發(fā)消息轉(zhuǎn)發(fā)調(diào)用 forwardInvocation的替換方法FWHook_forwardInvocation,在FWHook_forwardInvocation中依次調(diào)用before、alias_sayHello、after.
代碼實(shí)現(xiàn)
1 FWHookItem對(duì)block進(jìn)行代碼簽名
@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;
/*
方法調(diào)用
*/
-(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參數(shù)
@interface NSInvocation (FWArguments)
- (NSArray *)getParams;
@end
4 使用
typedef void (^ParamsBlock)(NSArray *params);
@interface NSObject (FWHook)
/*
selector : 需要監(jiān)聽的方法
option : 決定 block 執(zhí)行時(shí)機(jī),在selector的之前,之后,替代。
block :執(zhí)行的代碼塊 返回的params就是,selector中的參數(shù)
*/
-(void)hookSelector:(SEL)selector withOption:(FWHookOption)option usingBlock:(ParamsBlock)block;
+(void)hookSelector:(SEL)selector withOption:(FWHookOption)option usingBlock:(ParamsBlock)block;
@end