Runtime之Method Swizzling

前一阵子项目要求统计界面,即每进入一个界面就统计一次。想着这不就可以用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 

@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

至于更详细的大家可以参考 念茜 的博客

你可能感兴趣的:(Runtime之Method Swizzling)