iOS开发那些事Objective-C RuntimeRumeTime

iOS 里 Method Swizzing 的一个坑

2016-05-10  本文已影响2405人  alanwangmodify

今天项目上的一个功能频繁出现数组越界导致闪退的情况,这个功能上许多地方用了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,所以我对这份代码挺有信心的,但是实际运行的时候,objectAtIndexMy_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]吧。

上一篇下一篇

猜你喜欢

热点阅读