在上篇博客曾聊過(guò)對(duì)任意方法Swizzle有多種應(yīng)用,其中之一就是對(duì)多個(gè)方法的開始或者結(jié)束添加統(tǒng)一的切面調(diào)用。很有名的Aspect庫(kù)利用類似于KVO實(shí)現(xiàn)原理來(lái)實(shí)現(xiàn),在運(yùn)行時(shí)給需要Swizzle的類動(dòng)態(tài)添加子類,同時(shí)該對(duì)象isa指針指向創(chuàng)建的子類,然后hook子類的forwardInvocation關(guān)聯(lián)到函數(shù)__ASPECTS_ARE_BEING_CALLED__。之后再將要hook的method的實(shí)現(xiàn)替換為_objc_msgForward,這樣對(duì)該method調(diào)用會(huì)就轉(zhuǎn)發(fā)到__ASPECTS_ARE_BEING_CALLED__,然后利用NSInvocation轉(zhuǎn)發(fā)該調(diào)用即可。而有了對(duì)任意方法Swizzle的方案后就可以另辟蹊徑來(lái)解決這個(gè)問(wèn)題。
上篇博客給了個(gè)簡(jiǎn)單的demo實(shí)現(xiàn),不過(guò)功能較弱,這次花了一天多時(shí)間,寫了一個(gè)較為成熟的實(shí)現(xiàn),總共三百行左右,其中一半是匯編。
先上代碼,先嚇走一票人再說(shuō)。
#import <objc/runtime.h>
typedef NS_OPTIONS(NSInteger, ZWInvocationOption) {
ZWInvocationOptionReplace = 1,
ZWInvocationOptionBefore = 1 << 1,
ZWInvocationOptionAfter = 1 << 2,
ZWInvocationOptionOnly = 1 << 3,
};
#if defined(__arm64__)
static NSMutableDictionary *_ZWBeforeIMP;
static NSMutableDictionary *_ZWOriginIMP;
static NSMutableDictionary *_ZWAfterIMP;
static NSLock *_ZWLock;
OS_ALWAYS_INLINE void ZWStoreParams(void) {
asm volatile("str d7, [x11, #0x88]\n\
str d6, [x11, #0x80]\n\
str d5, [x11, #0x78]\n\
str d4, [x11, #0x70]\n\
str d3, [x11, #0x68]\n\
str d2, [x11, #0x60]\n\
str d1, [x11, #0x58]\n\
str d0, [x11, #0x50]\n\
str x8, [x11, #0x40]\n\
str x7, [x11, #0x38]\n\
str x6, [x11, #0x30]\n\
str x5, [x11, #0x28]\n\
str x4, [x11, #0x20]\n\
str x3, [x11, #0x18]\n\
str x2, [x11, #0x10]\n\
str x1, [x11, #0x8]\n\
str x0, [x11]\n\
");
}
OS_ALWAYS_INLINE void ZWLoadParams(void) {
asm volatile("ldr d7, [x11, #0x88]\n\
ldr d6, [x11, #0x80]\n\
ldr d5, [x11, #0x78]\n\
ldr d4, [x11, #0x70]\n\
ldr d3, [x11, #0x68]\n\
ldr d2, [x11, #0x60]\n\
ldr d1, [x11, #0x58]\n\
ldr d0, [x11, #0x50]\n\
ldr x8, [x11, #0x40]\n\
ldr x7, [x11, #0x38]\n\
ldr x6, [x11, #0x30]\n\
ldr x5, [x11, #0x28]\n\
ldr x4, [x11, #0x20]\n\
ldr x3, [x11, #0x18]\n\
ldr x2, [x11, #0x10]\n\
ldr x1, [x11, #0x8]\n\
ldr x0, [x11]\n\
");
}
OS_ALWAYS_INLINE void ZWGlobalOCSwizzle(void) {
asm volatile("stp x29, x30, [sp, #-0x10]!");
asm volatile("mov x29, sp\n\
sub sp, sp, #0xb0");
asm volatile("mov x11, sp");
asm volatile("bl _ZWStoreParams");
asm volatile("mov x0, sp");
asm volatile("bl _ZWBeforeInvocation");
asm volatile("mov x0, sp");
asm volatile("bl _ZWInvocation");
asm volatile("str x0, [sp, #0xa0]");
asm volatile("str d0, [sp, #0xa8]");
asm volatile("mov x0, sp");
asm volatile("bl _ZWAfterInvocation");
asm volatile("ldr x0, [sp, #0xa0]");
asm volatile("ldr d0, [sp, #0xa8]");
asm volatile("mov sp, x29");
asm volatile("ldp x29, x30, [sp], #0x10");
}
OS_ALWAYS_INLINE NSString *ZWGetMetaSelName(SEL sel) {
return [@"__META_" stringByAppendingString:NSStringFromSelector(sel)];
}
OS_ALWAYS_INLINE id ZWGetInvocation(NSDictionary *dict, id obj, SEL sel) {
Class class = object_getClass(obj);
NSString *className = NSStringFromClass(class);
NSString *selName = class_isMetaClass(class) ? ZWGetMetaSelName(sel) : NSStringFromSelector(sel);
[_ZWLock lock];
id Invocation = dict[className][selName];
[_ZWLock unlock];
return Invocation;
}
OS_ALWAYS_INLINE NSUInteger ZWGetInvocationCount(NSDictionary *dict, id obj, SEL sel) {
id ret = ZWGetInvocation(dict, obj, sel);
if ([ret isKindOfClass:[NSArray class]]) {
return [ret count];
} else if ([ret isKindOfClass:NSClassFromString(@"NSBlock")]) {
return 1;
}
return 0;
}
IMP ZWGetOriginImp(id obj, SEL sel) {
id Invocation = ZWGetInvocation(_ZWOriginIMP, obj, sel);
if ([Invocation isKindOfClass:[NSValue class]]) {
return [Invocation pointerValue];
}
return NULL;
}
IMP ZWGetCurrentImp(id obj, SEL sel) {
id Invocation = ZWGetInvocation(_ZWOriginIMP, obj, sel);
if ([Invocation isKindOfClass:NSClassFromString(@"NSBlock")]) {
if (!Invocation) return NULL;
uint64_t *p = (__bridge void *)(Invocation);
return (IMP)*(p + 2);
}
return NULL;
}
IMP ZWGetAopImp(NSDictionary *Invocation, id obj, SEL sel, NSUInteger index) {
if (!obj || !sel) return NULL;
id block = ZWGetInvocation(Invocation, obj, sel);
if ([block isKindOfClass:[NSArray class]]) {
block = block[index];
}
if (!block) return NULL;
uint64_t *p = (__bridge void *)(block);
return (IMP)*(p + 2);
}
void ZWAopInvocation(void **sp, NSDictionary *Invocation, ZWInvocationOption option) {
id obj = (__bridge id)(*sp);
SEL sel = *(sp + 1);
if (!obj || !sel) return;
NSInteger count = ZWGetInvocationCount(Invocation, obj, sel);
__autoreleasing NSArray *arr = @[obj, [NSValue valueWithPointer:sel], @(option)];
for (int i = 0; i < count; ++i) {
ZWGetAopImp(Invocation, obj, sel, i);
asm volatile("cbz x0, LBB_20181107");
asm volatile("mov x17, x0");
asm volatile("ldr x11, %0": "=m"(sp));
asm volatile("ldr x12, %0": "=m"(arr));
asm volatile("bl _ZWLoadParams");
asm volatile("mov x1, x12");
asm volatile("blr x17");
asm volatile("LBB_20181107:");
}
}
void ZWAfterInvocation(void **sp) {
ZWAopInvocation(sp, _ZWAfterIMP, ZWInvocationOptionAfter);
}
void ZWInvocation(void **sp) {
__autoreleasing id obj;
SEL sel;
void *obj_p = &obj;
void *sel_p = &sel;
asm volatile("ldr x11, %0": "=m"(sp));
asm volatile("ldr x10, %0": "=m"(obj_p));
asm volatile("ldr x0, [x11]");
asm volatile("str x0, [x10]");
asm volatile("ldr x10, %0": "=m"(sel_p));
asm volatile("ldr x0, [x11, #0x8]");
asm volatile("str x0, [x10]");
asm volatile("ldr x11, %0": "=m"(sp));
asm volatile("ldr x0, [x11]");
asm volatile("ldr x1, [x11, #0x8]");
asm volatile("bl _ZWGetOriginImp");
asm volatile("cbnz x0, LZW_20181105");
__autoreleasing NSArray *arr = @[obj, [NSValue valueWithPointer:sel], @(ZWInvocationOptionReplace)];
asm volatile("ldr x11, %0": "=m"(sp));
asm volatile("ldr x0, [x11]");
asm volatile("ldr x1, [x11, #0x8]");
asm volatile("bl _ZWGetCurrentImp");
asm volatile("cbz x0, LZW_20181106");
asm volatile("mov x17, x0");
asm volatile("ldr x12, %0": "=m"(arr));
asm volatile("ldr x11, %0": "=m"(sp));
asm volatile("bl _ZWLoadParams");
asm volatile("mov x1, x12");
asm volatile("blr x17");
asm volatile("b LZW_20181106");
asm volatile("LZW_20181105:");
asm volatile("mov x17, x0");
asm volatile("ldr x11, %0": "=m"(sp));
asm volatile("bl _ZWLoadParams");
asm volatile("blr x17");
asm volatile("LZW_20181106:");
}
void ZWBeforeInvocation(void **sp) {
ZWAopInvocation(sp, _ZWBeforeIMP, ZWInvocationOptionBefore);
}
OS_ALWAYS_INLINE Method ZWGetMethod(Class cls, SEL sel) {
unsigned int count = 0;
Method retMethod = NULL;
Method *list = class_copyMethodList(cls, &count);
for (int i = 0; i < count; ++i) {
Method m = list[i];
SEL s = method_getName(m);
if (sel_isEqual(s, sel)) {
retMethod = m;
}
}
free(list);
return retMethod;
}
OS_ALWAYS_INLINE void ZWAddInvocation(NSDictionary *dict, NSString *className, NSString *selName, id block, ZWInvocationOption options) {
NSArray *tmp = dict[className][selName];
if (options & ZWInvocationOptionOnly) {
dict[className][selName] = block;
} else {
if ([tmp isKindOfClass:[NSArray class]]) {
dict[className][selName] = [tmp arrayByAddingObject:block];
} else if (!tmp) {
dict[className][selName] = @[block];
}
}
}
void ZWAddAop(id obj, SEL sel, ZWInvocationOption options, id block) {
if (options == ZWInvocationOptionOnly || !obj || !sel || !block) return;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_ZWOriginIMP = [NSMutableDictionary dictionary];
_ZWBeforeIMP = [NSMutableDictionary dictionary];
_ZWAfterIMP = [NSMutableDictionary dictionary];
_ZWLock = [[NSLock alloc] init];
});
Class class = object_getClass(obj);
Method method = NULL;
NSString *className = NSStringFromClass(class);
ZWGetMethod(class, sel, &method);//class_getInstanceMethod(class, sel)會(huì)獲取父類的方法
NSString *selName = class_isMetaClass(class) ? ZWGetMetaSelName(sel) : NSStringFromSelector(sel);
[_ZWLock lock];
if (!_ZWOriginIMP[className]) {
_ZWOriginIMP[className] = [NSMutableDictionary dictionary];
_ZWBeforeIMP[className] = [NSMutableDictionary dictionary];
_ZWAfterIMP[className] = [NSMutableDictionary dictionary];
}
if (options & ZWInvocationOptionReplace) {
_ZWOriginIMP[className][selName] = block;
} else {
IMP originImp = method_getImplementation(method);
if (originImp != ZWGlobalOCSwizzle) {
_ZWOriginIMP[className][selName] = [NSValue valueWithPointer:originImp];
}
}
if (options & ZWInvocationOptionBefore) {
ZWAddInvocation(_ZWBeforeIMP, className, selName, block, options);
}
if (options & ZWInvocationOptionAfter) {
ZWAddInvocation(_ZWAfterIMP, className, selName, block, options);
}
method_setImplementation(method, ZWGlobalOCSwizzle);
[_ZWLock unlock];
}
#else
void ZWAddAop(id obj, SEL sel, ZWInvocationOption options, id block) {
}
#endif
這里只寫了arm64下的實(shí)現(xiàn),arm32機(jī)器已經(jīng)比較少,x86_64匯編比較麻煩,就不實(shí)現(xiàn)了。
入口函數(shù)ZWAddAop,前倆參數(shù)就不多說(shuō)了,第三個(gè)參數(shù)是一個(gè)block,這個(gè)block在定義的時(shí)候,一定要注意參數(shù)類型要對(duì)應(yīng),Method的前兩個(gè)參數(shù)簽名為固定的“@:”,而block第一個(gè)參數(shù)簽名是固定的“@?”,就是block本身,所以這里本來(lái)是需要一個(gè)占位的參數(shù)SEL和原始方法對(duì)應(yīng),后續(xù)的參數(shù)一一對(duì)應(yīng)即可,如果不需要使用某些參數(shù)可以省略聲明。但需要注意的是這里的占位參數(shù)被我改變成了NSArray,其會(huì)將當(dāng)前調(diào)用的對(duì)象和方法,以及調(diào)用位置封裝成數(shù)組傳遞,方便使用者。(簡(jiǎn)易實(shí)現(xiàn),所以直接用了公共容器)。另外需要說(shuō)明的是這里我并沒(méi)有校驗(yàn)block和原始方法簽名是否對(duì)應(yīng),需要的可以自行添加。
- (int )aMethod3:(NSString *)str;
對(duì)應(yīng)
^(NSArray *info, NSString *str) {};
如果不使用str, ^(NSArray *info){};不使用info,也可以不聲明
第四個(gè)參數(shù)ZWInvocationOption,表示需要在哪些位置插入該block調(diào)用。其中ZWInvocationOptionOnly不能單獨(dú)使用,其他情況都可以單獨(dú)使用或者復(fù)合使用。Only和Before,After復(fù)合使用時(shí)表示當(dāng)前添加的block是當(dāng)前位置唯一的調(diào)用,其他調(diào)用會(huì)被替換掉,否則在同一個(gè)方法的同一個(gè)位置可以添加多個(gè)block調(diào)用。Only和Replace復(fù)合沒(méi)有意義,因?yàn)槲矣X(jué)得沒(méi)有必要替換多次,因?yàn)閺墓δ苌现vBefore就可以完成這項(xiàng)需求,如果有需求的話可以自行修改。
ZWAddAop函數(shù)實(shí)現(xiàn)比較容易看懂,需要說(shuō)明一下的是,這里我操作NSMutableDictionary時(shí)我加了鎖,如果有大量方法需要替換時(shí),可以采用串行調(diào)用而不是加鎖的方式,這樣速度會(huì)提高。如果是元類方法會(huì)在存儲(chǔ)的時(shí)候添加上__META_頭和實(shí)例方法區(qū)分開。另外class_getInstanceMethod會(huì)獲取父類的實(shí)現(xiàn),所以這里class_copyMethodList來(lái)搜索,因?yàn)榇嬖趦?nèi)存拷貝,所以效率不太好。
注意:優(yōu)化點(diǎn),將所有NSDictionary的selector key由NSString變成NSNumber,該地址是一個(gè)常量,而class對(duì)象是可以直接作為key的。AOP切面調(diào)用極為頻繁,這就會(huì)帶來(lái)極大的性能提升,優(yōu)化后甚至可以提高一個(gè)數(shù)量級(jí)。
以上是添加AOP調(diào)用的過(guò)程,接下來(lái)說(shuō)明一下調(diào)用過(guò)程。
所有添加AOP的方法,IMP會(huì)修改為ZWGlobalOCSwizzle方法,其聲明沒(méi)有參數(shù)沒(méi)有返回值,也不涉及任何OC和C的代碼,所以編譯器會(huì)給一個(gè)空的實(shí)現(xiàn),不會(huì)添加多余代碼,可以方便的添加匯編代碼(這種叫C樁(stub)函數(shù))。其邏輯還是很容易看懂的。
我大概說(shuō)一下,這里我抽離了參數(shù)入棧的的匯編到ZWStoreParams,這個(gè)函數(shù)會(huì)被多個(gè)地方調(diào)用,所以不能直接使用sp,我這里使用了x11,調(diào)用的時(shí)候需要設(shè)置x11的值(復(fù)雜的情況下使用寄存器,需要先暫存其值到棧上,方便之后恢復(fù)供后續(xù)使用,只不過(guò)比較麻煩,會(huì)涉及到初始??臻g的分配,這里其他地方不會(huì)使用x11存數(shù)據(jù)就簡(jiǎn)單處理了),調(diào)用ZWStoreParams,將參數(shù)存在棧上,同時(shí)將棧指針?lè)湃離0,傳遞給ZWBeforeInvocation使用。
接下來(lái)調(diào)用ZWBeforeInvocation,其接收了棧指針sp,上面有所有參數(shù),其內(nèi)部調(diào)用ZWAopInvocation。
在ZWAopInvocation中獲枚舉當(dāng)前方法當(dāng)前位置所有添加的AOP block。下面代碼會(huì)將一些信息封裝到數(shù)組中,后面寫x1也就是block調(diào)用的第二個(gè)參數(shù)(info)。
__autoreleasing NSArray *arr = @[obj, [NSValue valueWithPointer:sel], @(option)];
for循環(huán)所有調(diào)用,并依次調(diào)用ZWGetAopImp獲取所有block入口地址,ZWGetAopImp在字典中_ZWBeforeIMP或者_ZWAfterIMP獲取存入的Block,并計(jì)算出入口地址返回。
使用cbz指令, 如果x0==0就跳轉(zhuǎn)標(biāo)簽LBB_20181107,就是末尾。否則將入口地址寫入x17,接下來(lái)加載sp指針到x11,加載arr到x12,調(diào)用ZWLoadParams加載參數(shù)到寄存器,將arr寫入x1,最后跳轉(zhuǎn)到x17執(zhí)行block方法。
note:標(biāo)簽LZW_20181107似乎有一些要求,不能隨便亂寫,似乎要L打頭(L表示局部標(biāo)簽),后面就隨便了,確保唯一即可。
回到ZWGlobalOCSwizzle函數(shù),調(diào)用完ZWBeforeInvocation后,調(diào)用ZWInvocation,也就是原方法或是其替換方法。該方法也接收sp參數(shù),然后我們聲明obj,sel,及其指針obj_p,sel_p,接下來(lái)將sp和obj,sel的地址加載到寄存器,然后將ps,ps+8,也就是棧上的第一第二個(gè)參數(shù)寫入obj,sel中。這樣就把匯編的數(shù)據(jù)轉(zhuǎn)移到了C語(yǔ)言的變量里面,這也是內(nèi)聯(lián)匯編和OC/C要數(shù)據(jù)交互方法。
接下來(lái)將sp,sp+8寫入x0,x1,然后調(diào)用ZWGetOriginImp,其會(huì)嘗試獲取_ZWOriginIMP字典中的原始IMP并返回,匯編cbnz x0, LZW_20181105如果原始IMP不為NULL,則跳轉(zhuǎn)標(biāo)簽LZW_20181105。
再看標(biāo)簽LZW_20181105,其實(shí)現(xiàn)也比較簡(jiǎn)單,將原始IMP寫入x17,同時(shí)加載sp到x11,調(diào)用ZWLoadParams恢復(fù)參數(shù)到寄存器上,然后跳轉(zhuǎn)到x17執(zhí)行原始IMP后就結(jié)束了(變量聲明都是autoreleasing的,所以不會(huì)插入release也就不會(huì)破壞x0,d0)。
如果原始IMP為NULL,則表明其被替換過(guò)。則將obj,sel,調(diào)用位置ZWInvocationOptionReplace封裝成數(shù)組arr待用。加載sp,sp+8到x0,x1,然后調(diào)用ZWGetCurrentImp,獲取替換后的實(shí)現(xiàn)。其是一個(gè)block,計(jì)算出IMP并返回。cbz x0, LZW_20181106,如果返回值為NULL,則直接跳轉(zhuǎn)到標(biāo)簽LZW_20181106結(jié)束調(diào)用,否則繼續(xù)執(zhí)行,將IMP寫入x17,加載arr,sp到x12,x11,調(diào)用ZWLoadParams,恢復(fù)所有的參數(shù),這里和調(diào)用原始IMP不一樣的時(shí)候?qū)⒌诙€(gè)參數(shù)x1替換為數(shù)組arr。然后blr x17執(zhí)行IMP,之后跳轉(zhuǎn)到標(biāo)簽LZW_20181106,這里不能直接使用匯編的"ret"返回。因?yàn)樵摼渲髸?huì)調(diào)用___stack_chk_guard來(lái)檢查棧有沒(méi)有被破壞,不過(guò)幸運(yùn)的是該調(diào)用沒(méi)有破壞x0,d0寄存器。
最后再次回到ZWGlobalOCSwizzle函數(shù),調(diào)用完ZWInvocation后,將可能存儲(chǔ)返回值的寄存器x0,d0寫入sp的+0xa0,+0xa8的位置,然后加載sp為參數(shù)調(diào)用ZWAfterInvocation,其邏輯和ZWBeforeInvocation基本一致就不再多說(shuō)。
然后從sp的+0xa0,+0xa8的位置恢復(fù)數(shù)據(jù)到x0,d0,然后恢復(fù)sp,x29,x30寄存器,最后返回。
怎么使用呢?這里給一些例子
- (void)viewDidLoad {
[super viewDidLoad];
ZWAddAop(self, @selector(aMethod1:), ZWInvocationOptionBefore | ZWInvocationOptionOnly, ^(NSArray *info){
NSLog(@"before1 : ");
});
ZWAddAop(self, @selector(aMethod1:), ZWInvocationOptionBefore | ZWInvocationOptionReplace | ZWInvocationOptionAfter , ^(NSArray *info, int a){
NSLog(@"before1 | replace1 | after1 : %d", a);
});
ZWAddAop(self, @selector(aMethod2:), ZWInvocationOptionAfter, ^(NSArray *info, NSString *str){
NSLog(@"after2: %@", str);
});
ZWAddAop(self, @selector(aMethod2:), ZWInvocationOptionReplace | ZWInvocationOptionOnly, ^(NSArray *info, NSString *str){
NSLog(@"replace2 | after2: %@ \n%@",info, str);
});
ZWAddAop(self, @selector(aMethod3::), ZWInvocationOptionReplace, ^int (NSArray *info, NSString *str){
NSLog(@"replace3: %@", str);
return 11034;
});
ZWAddAop(self, @selector(aMethod3::), ZWInvocationOptionAfter, ^(NSArray *info, NSString *str){
NSLog(@"after31: %@ \n %@", info, str);
});
ZWAddAop(self, @selector(aMethod3::), ZWInvocationOptionAfter, ^(NSArray *info, NSString *str){
NSLog(@"after32: %@", str);
});
ZWAddAop([self class], @selector(aMethod4:), ZWInvocationOptionReplace, ^(id info , int a){
NSLog(@"META replace4:");
});
[self aMethod1:8848];
[self aMethod2:@"test str"];
int r = [self aMethod3:@"咋不上天呢" :@[@1,@2]];
NSLog(@"return int: %d",r);
[ViewController aMethod4:12358];
}
- (NSRange)aMethod1:(int)a {
NSLog(@"method1: %d",a);
return (NSRange){0,1};
}
- (void)aMethod2:(NSString *)str {
NSLog(@"method2: %@", str);
}
- (int )aMethod3:(NSString *)str :(NSArray *)array{
NSLog(@"method3: %@", str);
return 11;
}
+ (void)aMethod4:(int )obj {
NSLog(@"method4: %d", obj);
}
本來(lái)只是想寫一個(gè)demo供大家參考的,但發(fā)現(xiàn)很多東西還是要過(guò)手了才有說(shuō)服力,于是前前后后(主要是想簡(jiǎn)單實(shí)現(xiàn)基本功能即可,后來(lái)發(fā)現(xiàn)很多功能特性還是得添加,于是乎就不斷涂涂改改...)花了兩天時(shí)間寫了個(gè)較完善些的實(shí)現(xiàn)。如果對(duì)穩(wěn)定性要求較高,可以多測(cè)試一下,匯編和OC/C混編確實(shí)容易出bug,寫的時(shí)候稍不注意就出現(xiàn)莫名其妙的問(wèn)題,需要不斷的修改和改進(jìn),另外我測(cè)試的case也不太足。
最后說(shuō)一下這套方案的優(yōu)缺點(diǎn)吧。
優(yōu)點(diǎn):1、簡(jiǎn)潔,雖然可能匯編細(xì)節(jié)不太懂,但大致的實(shí)現(xiàn)思路還是很容易看懂。
2、效率較高,大量使用匯編,減少函數(shù)調(diào)用,當(dāng)然也有一個(gè)龜速代表class_copyMethodList拖慢了效率,當(dāng)然也僅僅只是指針拷貝,而且大部分類的方法也就幾個(gè)十幾個(gè)。主要是沒(méi)有其他類似的方法,要替換class_copyMethodList就得自己訪問(wèn)Class的MethodList,這比較麻煩,需要大量的代碼才能實(shí)現(xiàn),不過(guò)class_copyMethodList調(diào)用不多。每個(gè)hook函數(shù)調(diào)用過(guò)程中AOP的開銷才是重點(diǎn)關(guān)注的地方,目前主要是內(nèi)存分配(OC對(duì)象創(chuàng)建)。
缺點(diǎn),改進(jìn)點(diǎn):
1、追求簡(jiǎn)潔,拋棄了一些細(xì)節(jié)問(wèn)題。比如傳入的block和原始方法的簽名沒(méi)有校驗(yàn)是否匹配,再比如:block的傳回的第一個(gè)參數(shù)使用NSArray來(lái)存儲(chǔ)obj,sel和調(diào)用位置,其實(shí)最好可以使用model對(duì)象,同時(shí)也可以傳回更多的信息。
2、整型參數(shù)大于6個(gè)或者浮點(diǎn)參數(shù)大于8個(gè)則無(wú)法處理。因?yàn)槎喑鰜?lái)的參數(shù)在棧上,目前我還沒(méi)有想出除了拷貝棧以外的簡(jiǎn)潔解決方案,而拷貝棧比較麻煩(我大概思考了一下,比較麻煩的部分是確定棧上存了多少數(shù)據(jù),這個(gè)得根據(jù)簽名和參數(shù)傳遞的規(guī)則去計(jì)算)。如果需要處理這類函數(shù)就需要自己另外處理了。