可以实现方法的交换,例如,用自己实现的 xxx_viewWillAppear:
代替系统的 viewWillAppear:
typedef struct objc_method *Method;
struct objc_method {
SEL _Nonnull method_name OBJC2_UNAVAILABLE;
char * _Nullable method_types OBJC2_UNAVAILABLE;
IMP _Nonnull method_imp OBJC2_UNAVAILABLE;
}
Thor
@interface Thor : NSObject
- (void)hammerFallStrike;
@end
@implementation Thor
- (void)hammerFallStrike {
NSLog(@"%@ hammerFall strikes", self);
}
@end
Thor *thor = [[Thor alloc] init];
NSLog(@"Thor hammerFall strikes:\n");
[thor hammerFallStrike];
// Thor hammerFall strikes:
// <Thor: 0x100608660> hammerFall strikes
Thor+Swizzling
@interface Thor (Swizzling)
- (void)whipStrike;
@end
#include <objc/runtime.h>
@implementation Thor (Swizzling)
- (void)whipStrike {
NSLog(@"%@, whip strikes", self);
}
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 1. 获取 swizzle 前的方法名
SEL originalSelector = @selector(hammerFallStrike);
SEL swizzledSelector = @selector(whipStrike);
Class class = [self class];
// 2. 获取 swizzle 前的方法定义
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
// 3. 尝试将 swizzledMethod 的实现添加到 originalSelector 的指向
BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
// 若上面的尝试成功,那就将 originalMethod 的实现添加到 swizzledSelector 的指向,保证不丢失原有的方法
class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
} else {
// 若上面的尝试不成功,就是 originalSelector 中的方法实现已经有东西存在了
// originSelector 的方法实现已经有东西了,那就直接将 originalSelector 和 swizzledSelector 的实现交换吧
method_exchangeImplementations(originalMethod, swizzledMethod);
}
});
}
@end
Thor *thor = [[Thor alloc] init];
NSLog(@"Thor hammerFall strikes:\n");
[thor hammerFallStrike];
// Thor hammerFall strikes:
// <Thor: 0x100730980>, whip strikes
可以看到,但我们需要 Thor 使用 hammerFall 攻击的时候,TA 却耍起了 whip, 调用了同样的方法,得到了不同的结果
⚠️ 应该只在 + (void)load
中完成
在运行时,每个类都有两个方法会自动调用,在方法有实现的情况下
+ (void)load
类被初始装载时被调用+ (void)initialize
应用第一次调用类的类方法或实例方法前调用⚠️ 应该只在 dispatch_once
中完成
Method Swizzling 改变了全局的状态,采用单例的标准进行 Method Swizzling 保证了交换代码只执行一个,保证线程安全
- (void)xxx_viewWillAppear:(BOOL)animated {
[self xxx_viewWillAppear:animated];
NSLog(@"viewWillAppear: %@", NSStringFromClass([self class]));
}
viewWillAppear:
进行 Method Swizzling, 便会死循环viewWillAppear:
进行了 Method Swizzling 后
viewWillAppear:
的实现指向了 xxx_viewWillAppear:
的实现xxx_viewWillAppear:
的实现指向了 viewWillAppear:
的实现viewWillAppear:
时,实际上执行的是 xxx_viewWillAppear:
的代码xxx_viewWillAppear:
时,实际上执行的是 viewWillAppear:
的代码