解决的Objective-C的hook方案: Method Swizzling

2017-01-11 16:00:29来源:http://www.jianshu.com/p/47df5bf1e9d7作者:dylan_人点击

今天说下写代码时遇到的坑。如标题所说。在hook时候发现的诡异问题


BOOL ClassMethodSwizzle(Class aClass, SEL originalSelector, SEL swizzleSelector ) {
Method originalMethod = class_getInstanceMethod(aClass, originalSelector);
Method swizzleMethod = class_getInstanceMethod(aClass, swizzleSelector);
method_exchangeImplementations(originalMethod, swizzleMethod);
return YES;
}

首先。定义了一个C方法。需要传参数三个。aClass是你要hook的类。originalSelector是你原有的@selector()swizzleMethod则是你想要替换的@selector()


正常来讲我们只需要替换两个@selector()的实现既可以达到hook的目的。



屏幕快照 2016-12-15 上午10.50.34.png

但是!!!
例如。如果我们想要替代的方法没有被实现怎么办。举个栗子。


假如有个控制器ViewController是这个样子的




我们想hook他的viewWillAppear:方法。我们按照没有改动前hook的话是这样的。




然后你会发现你所写的rp_viewWillAppear:不会被调用。具体原因我就不在这里分析了。太浪费时间。有需要的话可以自己打印查看分析。


IMP originalIMP = method_getImplementation(originalMethod);
IMP swizzleIMP = method_getImplementation(originalMethod);

于是乎。增强版hook方法出现了。


BOOL ClassMethodSwizzle(Class aClass, SEL originalSelector, SEL swizzleSelector ) {
Method originalMethod = class_getInstanceMethod(aClass, originalSelector);
Method swizzleMethod = class_getInstanceMethod(aClass, swizzleSelector);
BOOL didAddMethod =
class_addMethod(aClass,
originalSelector,
method_getImplementation(swizzleMethod),
method_getTypeEncoding(swizzleMethod));
if (didAddMethod) {
class_replaceMethod(aClass,
swizzleSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzleMethod);
}
return YES;
}

上面是大多数的博客写的针对Objective-C的hook方案。
先判断aClass有没有实现originalSelector:方法。如果有则做替换操作(上面有讲过)


如果没有则添加originalSelector方法。同时修改originalSelector的实现地址为swizzleSelector的地址。对应的代码为


BOOL didAddMethod = class_addMethod(aClass,
originalSelector,
method_getImplementation(swizzleMethod),
method_getTypeEncoding(swizzleMethod));

他会返回一个BOOL值。如果BOOL值为NO。意思是aClass已经实现originalSelector方法。添加方法失败。如果BOOL值为YES。则是为aClass增加了方法originalSelector。同时实现地址为swizzleMethod


于是就变成这个样子了




接下来我们只需要将selector_swizzle指向一个空实现就可以解决问题。


class_replaceMethod(aClass,
swizzleSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));

这行代码的作用是希望变成这个样子




但实际上他还是这个样子




所以我也不确定到底是谁的问题。总之我会出bug




rp_viewWillAppear会死循环。


于是乎我修改了hook实现


BOOL ClassMethodSwizzle(Class aClass, SEL originalSelector, SEL swizzleSelector ,SEL emptySelector) {
Method originalMethod = class_getInstanceMethod(aClass, originalSelector);
Method swizzleMethod = class_getInstanceMethod(aClass, swizzleSelector);
Method emptyMethod = class_getInstanceMethod(aClass, emptySelector);
BOOL didAddMethod =
class_addMethod(aClass,
originalSelector,
method_getImplementation(swizzleMethod),
method_getTypeEncoding(swizzleMethod));
if (didAddMethod) {
class_replaceMethod(aClass,
swizzleSelector,
method_getImplementation(emptyMethod),
method_getTypeEncoding(emptyMethod));
} else {
method_exchangeImplementations(originalMethod, swizzleMethod);
}
return YES;
}

修改了下代码




变成了这个样子




完美的解决了我的问题。




最新文章

123

最新摄影

微信扫一扫

第七城市微信公众平台