前一阵子项目要求统计界面,即每进入一个界面就统计一次。想着这不就可以用Method Swizzling。说起Method Swizzling ,简而言之就是交换方法。
说起怎么用它,先说说他的原理吧!
既然是交换方法,那是怎么交换的呢?其实Method Swizzling 本质上就是对IMP和SEL进行交换,首先你得知道Method Swizzling发生在运行时,在运行的时将两个Method进行交换。oc在runtime特性中,调用一个对象的方法就是给这个对象发送消息。是通过查找接收消息对象的方法列表,从方法列表中查找对应的SEL,这个SEL对应着一个IMP(一个IMP可以对应多个SEL),通过这个IMP找到对应的方法调用。
知道的原理,那就开始实现了,直接写个UIViewController的分类
+ (void)load {
[super load];
//class_getInstanceMethod 从当前独享中的method list获取method结构体;
//
Method fromMethod = class_getInstanceMethod([self class], @selector(viewDidLoad));
Method toMethod = class_getInstanceMethod([self class], @selector(chhSwizzlingViewDidLoad));;
// 验证class_addMethod()函数对Method Swizzling ,如果没有实现交换则失败,反之class_addMethod()将返回NO,即可以交换了。
if (!class_addMethod([self class], @selector(viewDidLoad), method_getImplementation(fromMethod), method_getTypeEncoding(toMethod))) {
method_exchangeImplementations(fromMethod, toMethod);
}
}
– (void)chhSwizzlingViewDidLoad {
NSString *str = [NSString stringWithFormat:@”%@”,self.class];
//将系统的UIViewController剔除掉
if (![str containsString:@”UI”]) {
NSLog(@”统计进入界面%@”,self.class);
}
[self chhSwizzlingViewDidLoad];
}
想到这里,我就说下NSArray,数组越界很容易碰到,但是想到一个越界苹果竟然crash,太严谨了。这时我们可以用Method swizzling 来交换objectAtIndex:的方法,再抛出异常,毕竟崩溃太吓人了。这里有一点要注意,NSArray是类簇,不能直接[self class]; 数组越界时候崩溃我们可以明显看到是__NSArrayI在调用 objectAtIndex: 方法。
下面直接贴代码
#import “NSArray+MethodSwizzling.h”
#import <objc/runtime.h>
@implementation NSArray (MethodSwizzling)
+ (void)load {
[super load];
Method fromMethod = class_getInstanceMethod(objc_getClass(“__NSArrayI”), @selector(objectAtIndex:));
Method toMethod = class_getInstanceMethod(objc_getClass(“__NSArrayI”), @selector(chhSwizzlingObjectAtIndex:));
if (!class_addMethod(objc_getClass(“__NSArrayI”), @selector(objectAtIndex:), method_getImplementation(toMethod), method_getTypeEncoding(toMethod))) {
method_exchangeImplementations(fromMethod, toMethod);
}
}
– (instancetype)chhSwizzlingObjectAtIndex:(NSUInteger)index {
if (self.count < index) {
@try {
return [self chhSwizzlingObjectAtIndex:index];
}
@catch (NSException *exception) {
NSLog(@”%s crash Because Method %s”,class_getName(self.class), __func__);
return NULL;
}
@finally {
}
}else {
return [self chhSwizzlingObjectAtIndex:index];
}
}
@end
既然能交换方法,那么Method Swizzling确实很危险,大家使用一定要注意
Method swizzling is not atomic
Changes behavior of un-owned code
Possible naming conflicts
Swizzling changes the method’s arguments
The order of swizzles matters
Difficult to understand (looks recursive)
Difficult to debug
至于更详细的大家可以参考 念茜 的博客