开发中总会遇到需要使用Method Swizzling的时候,记录一下Method Swizzling的正确方法
一、方法
以hookUIViewController
为例
#import <objc/runtime.h>
@implementation UIViewController (Tracking)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
SEL originalSelector = @selector(viewWillAppear:);
SEL swizzledSelector = @selector(xxx_viewWillAppear:);
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
// When swizzling a class method, use the following:
// Class class = object_getClass((id)self);
// ...
// Method originalMethod = class_getClassMethod(class, originalSelector);
// Method swizzledMethod = class_getClassMethod(class, swizzledSelector);
BOOL didAddMethod =
class_addMethod(class,
originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
class_replaceMethod(class,
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
});
}
#pragma mark - Method Swizzling
- (void)xxx_viewWillAppear:(BOOL)animated {
[self xxx_viewWillAppear:animated];
NSLog(@"viewWillAppear: %@", self);
}
@end
上面是一个通用的方式,会先尝试添加要hook的Method
(以swizzledMethod
的IMP
),如果添加成功,再把swizzledSelector
的实现替换为originalMethod
的,这样就完成了两个方法实现的互换。如果添加失败(即该class
已经存在对应Method
),则直接交换两个Method
的实现。
对于UIViewController
这样的已经确定有viewWillAppear:
方法的类,当然可以偷懒直接使用method_exchangeImplementations
来完成,但还是以上面的方式更为严谨。
二、+load vs. +initialize
Method Swizzling永远应该在+load方法中完成
+initialize
,仅会在该类的方法或者它的实例第一次被调用前,+initialize
方法被调用。即它有可能不被调用。
+load
方法会在main
函数运行前调用,每个类、分类的load
方法都会被调用。被调用时,所有的 framework 都已经加载到了运行时中(但有的类可能还未加载)。在一个类的 load
方法中调用其他类的方法,如果被调用的类还未load
,并不会触发被调用的类的load
方法。
三、dispatch_once
为了尽可能地保证Method Swizziling
只进行一次,代码应该包含在dispatch_once
中。
四、第三方
GitHub上有类似Aspect 这样的第三方,封装了Method Swizzling
方法,并提供了其他功能,如选择代码在原方法前或后执行,或者是直接替换原方法。Hook一个对象、一个类等。
一些第三方统计明显也是使用了类似的方法来实现统计的功能。
五、参考
-
Method Swizzling -- Mattt Thompson,详细讲了
Method Swizzling
的使用。 - 你真的了解load方法么?-- Draveness,解析了load方法的原理
-
load 和 initialize 方法的执行顺序以及类和对象的关系,各个类
load
方法调用顺序会与compile source
中文件顺序有关系
网友评论