如果你尝试hookperformSelector:withObject:
并swzzle
该方法,你会发现一运行很快就会崩溃,爆出EXC_BAD_ACCESS
野指针错误。
下面上demo:
#import "NSObject+Hock.h"
#import <objc/runtime.h>
@implementation NSObject (Hock)
+ (void)swizzleInstanceSelector:(SEL)originalSelector withSwizzledInstanceSelector:(SEL)swizzledSelector
{
Method originalMethod = class_getInstanceMethod(self, originalSelector);
Method swizzledMethod = class_getInstanceMethod(self, swizzledSelector);
if (originalMethod && swizzledMethod) {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self swizzleInstanceSelector:@selector(performSelector:withObject:) withSwizzledInstanceSelector:@selector(hook_performSelector:withObject:)];
});
}
- (id)hook_performSelector:(SEL)aSelector withObject:(id)object {
NSLog(@"------------hook: object:%@ aSelector:%@--------------",object,NSStringFromSelector(aSelector));
return [self hook_performSelector:aSelector withObject:object];
}
最后一次打印的内容是:
------------hook: object:<UIWindow: 0x7ffc0060a7e0; frame = (0 0; 375 667); autoresize = W+H; gestureRecognizers = <NSArray: 0x60000005bdb0>; layer = <UIWindowLayer: 0x600000030a00>> aSelector:setWindow:--------------
再往下走就崩溃了(黑人脸)!!!
先看看UIWindow
的setWindow:
方法,这个方法
在 AppDelegate.m
跑一下如下的代码:
SEL sel = NSSelectorFromString(@"setWindow:");
NSMethodSignature *signature = [[self.window class] instanceMethodSignatureForSelector:sel];
NSLog(@"%lu",(unsigned long)signature.methodReturnLength);
NSLog(@"%s",signature.methodReturnType);
上面的代码就是通过NSMethodSignature
来确定方法是否有返回值以及返回值的类型是什么。
打印结果如下:
2017-xx-xx 16:48:28.675 Demo[7521:1127858] 0
2017-xx-xx 16:48:30.180 Demo[7521:1127858] (null)
可以看到setWindow:
是没有返回值的,崩溃关返回值什么事?是的,就是关返回值的事。
在ARC
环境下当调用一个方法时,需要知道这个方法的返回值类型及返回值的接受者是谁,这也是ACR
下能够管理引用类型的一个重要因素。
在执行 performSelector:withObject:
方法时,该方法的返回值类型是id
类型,而实际上selector
方法的返回值有可能是void
,如本例中的setWindow:
,也就是ARC
不知道返回值是什么类型的。
在这种返回值是void
的情况下,ARC
视图返回一个值,但是实际上没有任何东西返回,当强制这么干的时候就会造成野指针。要解决这个问题可以兼容到MRC
,在MRC
下会根据实际情况决定是否返回一个值。
怎么兼容MRC
呢?前往 Build Phases > Compile Sources 给 NSObject+Hock.m
文件 加上-fno-objc-arc
再次运行,It is work!
整个过程就是如上所示,这也是解决hookperformSelector:withObject:
过程中可能奔溃的方法。