iOS 里 Method Swizzing 的一个坑
今天项目上的一个功能频繁出现数组越界导致闪退的情况,这个功能上许多地方用了NSArray的objectAtIndex:
方法,于是我想着用Method Swizzing 来hook一下这个方法,加上防越界判断,代码如下:
+(void)load{
static dispatch_once_t onceToken ;
dispatch_once(&onceToken, ^{
Method m1 = class_getInstanceMethod([self class], @selector(objectAtIndex:));
Method m2 = class_getInstanceMethod([self class], @selector(My_objectAtIndex:));
BOOL isAdd = class_addMethod([self class], @selector(objectAtIndex:), method_getImplementation(m2), method_getTypeEncoding(m2));
if (isAdd == YES) {
class_replaceMethod([self class], @selector(My_objectAtIndex:), method_getImplementation(m1), method_getTypeEncoding(m1));
}else{
method_exchangeImplementations(m1, m2);
}
});
}
-(id)My_objectAtIndex:(NSUInteger)index{
NSLog(@"exchangeSuccess");
return [self My_objectAtIndex:index];
}
因为写过许多次Method Swizzing,所以我对这份代码挺有信心的,但是实际运行的时候,objectAtIndex
和My_objectAtIndex
没有调换过来。
然后我检查了无数次代码,尝试了无数次可能,没有任何进展。
最后我开始怀疑是不是这个方法不支持hook,于是我hook了lastObject
方法,lastObject
方法。
+(void)load{
static dispatch_once_t onceToken ;
dispatch_once(&onceToken, ^{
Method m1 = class_getInstanceMethod([self class], @selector(lastObject));
Method m2 = class_getInstanceMethod([self class], @selector(My_lastObject));
BOOL isAdd = class_addMethod([self class], @selector(lastObject), method_getImplementation(m2), method_getTypeEncoding(m2));
if (isAdd == YES) {
class_replaceMethod([self class], @selector(My_lastObject), method_getImplementation(m1), method_getTypeEncoding(m1));
}else{
method_exchangeImplementations(m1, m2);
}
Method m3 = class_getInstanceMethod([self class], @selector(firstObject));
Method m4 = class_getInstanceMethod([self class], @selector(My_fistObjcet));
method_exchangeImplementations(m3, m4);
});
}
-(id)My_fistObjcet{
NSLog(@"My_fistObjcet : exchangeSuccess");
return [self My_fistObjcet];
}
-(id)My_lastObject{
NSLog(@"My_lastObject : exchangeSuccess");
return [self My_lastObject];
}
两个方法的Method swizzing都成功了。然后在My_lastObject上打断点的时候发现,在用ObjectAtIndex的时候,也调用了lastObject。
我又去尝试Method swizzing了几个用到Index的方法,而且都调用了lastObject,我查阅了一些文档,发现都是擦边讲到相关内容,大概原因是因为内部算法的限制,导致了用到Index的方法,大部分不支持Method Swizzing 。
因为用了比较久的时间去纠结,所以觉得有必要在这里Mark出这个坑。
----------- 分割线 ---------------
在大神们的帮助下找到了原因,并不是这里不支持Method Swizzing,而是我用错了类名 。之前用了 __NSArray0 试过没效果,以为是没什么关系的,也是因为自身的知识局限。后面用__NSArrayM,__NSArrayI便可以了。
Method m1 = class_getInstanceMethod(NSClassFromString(@"__NSArrayM"), @selector(objectAtIndex:));
Method m2 = class_getInstanceMethod(NSClassFromString(@"__NSArrayM"), @selector(My_objectAtIndex:));
我尝试去翻之前看的文章,但是找不到了。
因为文章是英文的,所以我可能有些理解错误。里面有一句“NSArray can not do this directly。” 这里的do this 是指Method Swizzing,现在想应该是说只不能直接用[self class]吧。