AOP:通過預(yù)編譯方式和運(yùn)行期動(dòng)態(tài)代理實(shí)現(xiàn)程序功能的統(tǒng)一維護(hù)的一種技術(shù)锄禽。
AOP對(duì)業(yè)務(wù)處理過程中的切面進(jìn)行提取,他所面對(duì)的是處理過程中的某個(gè)步驟或階段箱蟆。以獲得邏輯過程中各部分之間的耦合性的隔離效果沟绪。
OOP和AOP屬于兩個(gè)不同的“思考方式”。OOP專注于對(duì)象的屬性和行為的封裝空猜,AOP專注于處理某個(gè)步驟和階段的绽慈,從中進(jìn)行切面的提取恨旱。
講解:
Aspects是一個(gè)輕量級(jí)的面向切面編程的庫(kù)。它能允許你在每一個(gè)類和每一個(gè)實(shí)例中存在的方法里面加入任何代碼坝疼∷严停可以在以下切入點(diǎn)插入代碼:before(在原始的方法前執(zhí)行) / instead(替換原始的方法執(zhí)行) / after(在原始的方法后執(zhí)行,默認(rèn))。通過Runtime消息轉(zhuǎn)發(fā)實(shí)現(xiàn)Hook钝凶。Aspects會(huì)自動(dòng)的調(diào)用super方法仪芒,使用method swizzling起來(lái)會(huì)更加方便。
Aspects 的方案就是耕陷,對(duì)于待 hook 的 selector掂名,將其指向 objc_msgForward / _objc_msgForward_stret ,同時(shí)生成一個(gè)新的 aliasSelector 指向原來(lái)的 IMP,并且 hook 住 forwardInvocation 函數(shù)哟沫,使他指向自己的實(shí)現(xiàn)饺蔑。按照上面的思路,當(dāng)被 hook 的 selector 被執(zhí)行的時(shí)候嗜诀,首先根據(jù) selector 找到了 objc_msgForward / _objc_msgForward_stret ,而這個(gè)會(huì)觸發(fā)消息轉(zhuǎn)發(fā)猾警,從而進(jìn)入 forwardInvocation。同時(shí)由于 forwardInvocation 的指向也被修改了隆敢,因此會(huì)轉(zhuǎn)入新的 forwardInvocation 函數(shù)发皿,在里面執(zhí)行需要嵌入的附加代碼,完成之后拂蝎,再轉(zhuǎn)回原來(lái)的 IMP穴墅。
swizzing forwardInvocation:
aspect_hookClass 函數(shù)主要swizzing類/對(duì)象的forwardInvocation函數(shù)。aspects的真正的處理邏輯都是在forwardInvocation函數(shù)里面進(jìn)行的温自。對(duì)于對(duì)象實(shí)例而言封救,源代碼中并沒有直接 swizzling 對(duì)象的 forwardInvocation 方法,而是動(dòng)態(tài)生成一個(gè)當(dāng)前對(duì)象的子類捣作,并將當(dāng)前對(duì)象與子類關(guān)聯(lián),然后替換子類的 forwardInvocation 方法(這里具體方法就是調(diào)用了 object_setClass(self, subclass) ,將當(dāng)前對(duì)象 isa 指針指向了 subclass ,同時(shí)修改了 subclass 以及其 subclass metaclass 的 class 方法,使他返回當(dāng)前對(duì)象的 class。,這個(gè)地方特別繞鹅士,它的原理有點(diǎn)類似 kvo 的實(shí)現(xiàn)券躁,它想要實(shí)現(xiàn)的效果就是,將當(dāng)前對(duì)象變成一個(gè) subclass 的實(shí)例掉盅,同時(shí)對(duì)于外部使用者而言也拜,又能把它繼續(xù)當(dāng)成原對(duì)象在使用,而且所有的 swizzling 操作都發(fā)生在子類趾痘,這樣做的好處是你不需要去更改對(duì)象本身的類慢哈,也就是,當(dāng)你在 remove aspects 的時(shí)候永票,如果發(fā)現(xiàn)當(dāng)前對(duì)象的 aspect 都被移除了卵贱,那么滥沫,你可以將 isa 指針重新指回對(duì)象本身的類,從而消除了該對(duì)象的 swizzling ,同時(shí)也不會(huì)影響到其他該類的不同對(duì)象)键俱。對(duì)于每一個(gè)對(duì)象而言兰绣,這樣的動(dòng)態(tài)對(duì)象只會(huì)生成一次,這里 aspect_swizzlingForwardInvocation 將使得 forwardInvocation 方法指向 aspects 自己的實(shí)現(xiàn)邏輯 ,
swizzling selector
當(dāng) forwradInvocation 被 hook 之后编振,接下來(lái)缀辩,將對(duì)傳入的 selector 進(jìn)行 hook ,這里的做法是踪央,將 selector 指向了轉(zhuǎn)發(fā) IMP 臀玄,同時(shí)生成一個(gè) aliasSelector ,指向了原來(lái)的 IMP ,同時(shí)為了防止重復(fù) hook ,做了一個(gè)判斷畅蹂,如果發(fā)現(xiàn) selector 已經(jīng)指向了轉(zhuǎn)發(fā) IMP ,那就就不需要進(jìn)行交換了
這里講解下幾個(gè)函數(shù)的用法
// 添加方法
/**
第一個(gè)參數(shù): cls:給哪個(gè)類添加方法
第二個(gè)參數(shù): SEL name:添加方法的名稱
第三個(gè)參數(shù): IMP imp: 方法的實(shí)現(xiàn)健无,函數(shù)入口,函數(shù)名可與方法名不同(建議與方法名相同)
第四個(gè)參數(shù): types :方法類型魁莉,需要用特定符號(hào)睬涧,參考API
*/
BOOL class_addMethod ( Class cls, SEL name, IMP imp, const char *types );
替換 class 的 sel 對(duì)應(yīng)的函數(shù)指針,返回值為 sel 對(duì)應(yīng)的原函數(shù)指針
class_replaceMethod(Class cls, SEL name, IMP imp, const char * types)
直接替換 method 的函數(shù)指針
method_setImplementation(Method method, IMP imp)
NSMethodSignature和 NSInvocation進(jìn)行 method 或 block的調(diào)用
NSMethodSignature概述:
NSMethodSignature用于描述method的類型信息:返回值類型旗唁,以及每個(gè)參數(shù)的類型畦浓。
//獲取方法簽名
-(void)test
{
//獲取方法簽名
NSMethodSignature *sigOfPrintStr = [self methodSignatureForSelector:@selector(printStr:)];
//獲取方法簽名對(duì)應(yīng)的invocation
NSInvocation *invocationPrint = [NSInvocation invocationWithMethodSignature:sigOfPrintStr];
//設(shè)置消息的接收者
[invocationPrint setTarget:self];
[invocationPrint setSelector:@selector(printStr:)];
//設(shè)置參數(shù)
//對(duì)NSInvocation對(duì)象設(shè)置的參數(shù)個(gè)數(shù)及類型和獲取的返回值的類型要與創(chuàng)建對(duì)象時(shí)使用的NSMethodSignature對(duì)象代表的參數(shù)及返回值類型向一致,否則
NSString *str = @"helloWord" ;
[invocationPrint setArgument:&str atIndex:2];
[invocationPrint invoke];
//Block調(diào)用方式
void(^block1)(int) = ^(int a)
{
NSLog(@"block1 %d",a);
};
NSMethodSignature *signature = aspect_blockMethodSignature(block1,nil);
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
[invocation setTarget:block1];
int a=2;
//由block生成的NSInvocation對(duì)象的第一個(gè)參數(shù)是block本身检疫,剩下的為 block自身的參數(shù)讶请。
[invocation setArgument:&a atIndex:1];
[invocation invoke];
}
-(void)printStr:(NSString *)str
{
NSLog(@"打印%@",str);
}
//代碼來(lái)自 Aspect
// Block internals.
typedef NS_OPTIONS(int, AspectBlockFlags) {
AspectBlockFlagsHasCopyDisposeHelpers = (1 << 25),
AspectBlockFlagsHasSignature = (1 << 30)
};
typedef struct _AspectBlock {
__unused Class isa;
AspectBlockFlags flags;
__unused int reserved;
void (__unused *invoke)(struct _AspectBlock *block, ...);
struct {
unsigned long int reserved;
unsigned long int size;
// requires AspectBlockFlagsHasCopyDisposeHelpers
void (*copy)(void *dst, const void *src);
void (*dispose)(const void *);
// requires AspectBlockFlagsHasSignature
const char *signature;
const char *layout;
} *descriptor;
// imported variables
} *AspectBlockRef;
static NSMethodSignature *aspect_blockMethodSignature(id block, NSError **error) {
AspectBlockRef layout = (__bridge void *)block;
if (!(layout->flags & AspectBlockFlagsHasSignature)) {
//NSString *description = [NSString stringWithFormat:@"The block %@ doesn't contain a type signature.", block];
//AspectError(AspectErrorMissingBlockSignature, description);
return nil;
}
void *desc = layout->descriptor;
desc += 2 * sizeof(unsigned long int);
if (layout->flags & AspectBlockFlagsHasCopyDisposeHelpers) {
desc += 2 * sizeof(void *);
}
if (!desc) {
NSString *description = [NSString stringWithFormat:@"The block %@ doesn't has a type signature.", block];
//AspectError(AspectErrorMissingBlockSignature, description);
return nil;
}
const char *signature = (*(const char **)desc);
return [NSMethodSignature signatureWithObjCTypes:signature];
}
Objective-C Method 的 Type 信息以 “返回值 Type + 參數(shù) Types” 的形式組合編碼,還需要考慮到 self 和 _cmd 這兩個(gè)隱含參數(shù):
AspectInfo 類講解
把外面?zhèn)鬟M(jìn)來(lái)的實(shí)例instance屎媳,和原始的invocation保存到AspectInfo類對(duì)應(yīng)的成員變量中夺溢。
- (NSArray *)arguments方法是一個(gè)懶加載,返回的是原始的invocation里面的aspects參數(shù)數(shù)組烛谊。
Type Encodings作為對(duì)RunTime的補(bǔ)充,編譯器將每個(gè)方法的返回值和參數(shù)類型編碼為一個(gè)字符串风响,并將其與方法的selector關(guān)聯(lián)一起。
-
(int)tapWithView:(double)pointx; => "i@:d"
[圖片上傳失敗...(image-1d56c6-1534995690199)]
AspectInfo里面主要是 NSInvocation 信息丹禀。將NSInvocation包裝一層状勤,比如參數(shù)信息等。
AspectIdentifier講解
AspectIdentifier是一個(gè)切片Aspect的具體內(nèi)容双泪。里面會(huì)包含了單個(gè)的 Aspect 的具體信息持搜,包括執(zhí)行時(shí)機(jī),要執(zhí)行 block 所需要用到的具體信息:包括方法簽名焙矛、參數(shù)等等葫盼。初始化AspectIdentifier的過程實(shí)質(zhì)是把我們傳入的block打包成AspectIdentifier祟昭。
方法流程:
- aspect_hookSelector:(SEL)selector withOptions:(AspectOptions)options usingBlock:(id)block error:(NSError **)error
└── aspect_add(self, selector, options, block, error);
└── aspect_performLocked
├── aspect_isSelectorAllowedAndTrack
└── aspect_prepareClassAndHookSelector
> 生成一個(gè)AspectIdentifier
> 獲取到blockSignature
AspectsContainer講解
按照切面的時(shí)機(jī)分別把切片Aspects放到對(duì)應(yīng)的數(shù)組里面署隘。removeAspects會(huì)循環(huán)移除所有的Aspects。
AspectTracker講解
AspectTracker這個(gè)類是用來(lái)跟蹤要被hook的類
objc_setAssociatedObject(self, aliasSelector, aspectContainer, OBJC_ASSOCIATION_RETAIN);
四個(gè)參數(shù) 源對(duì)象 關(guān)鍵字 關(guān)聯(lián)的對(duì)象 關(guān)聯(lián)策略
五. Aspects hook過程詳解
- aspect_prepareClassAndHookSelector(self, selector, error);
├── aspect_hookClass(self, error)
│ ├──aspect_swizzleClassInPlace
│ ├──aspect_swizzleForwardInvocation
│ │ └──__ASPECTS_ARE_BEING_CALLED__
│ │ ├──aspect_aliasForSelector
│ │ ├──aspect_getContainerForClass
│ │ ├──aspect_invoke
│ │ └──aspect_remove
│ └── aspect_hookedGetClass
├── aspect_isMsgForwardIMP
├──aspect_aliasForSelector(selector)
└── aspect_getMsgForwardIMP
[圖片上傳失敗...(image-f94b38-1534995690200)]
傳送門
Demo注解: https://github.com/laotang013/AspectsDemo.git
https://halfrost.com/ios_aspect/
https://wereadteam.github.io/2016/06/30/Aspects/
http://www.reibang.com/p/cd431dc5fd6e
http://www.reibang.com/p/a6b675f4d073