Method swizzling就是在运行时改变方法的实现。
Method swizzling is the process of changing the implementation of an existing selector at runtime. Simply speaking, we can change the functionality of a method at runtime.
OC中的Method是一个C结构体,定义如下:
struct objc_method {
SEL _Nonnull method_name OBJC2_UNAVAILABLE;
char * _Nullable method_types OBJC2_UNAVAILABLE;
IMP _Nonnull method_imp OBJC2_UNAVAILABLE;
}
回顾一下OC中使用method swizzling
@interface Father : NSObject
@end
@implementation Father
+ (void)load{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class aClass = [self class];
SEL originSelector = @selector(makeMoney);
SEL swizzledSelector = @selector(swizzle_makeMoney);
Method originMethd = class_getInstanceMethod(aClass, originSelector);
Method swizzleMethod = class_getInstanceMethod(aClass, swizzledSelector);
BOOL didAdddMethod = class_addMethod(aClass, originSelector, method_getImplementation(swizzleMethod), method_getTypeEncoding(swizzleMethod));
if (didAdddMethod) {
class_replaceMethod(aClass, swizzledSelector, method_getImplementation(originMethd), method_getTypeEncoding(originMethd));
} else {
method_exchangeImplementations(originMethd, swizzleMethod);
}
});
}
- (void)makeMoney { NSLog(@"make money"); }
- (void)swizzle_makeMoney { NSLog(@"have a rest and make money"); }
Swift中使用 method swizzling
- 传统方法
要在Swift自定义类中使用method swizzling有两个必要条件
- 包含swizzle方法的类,需继承自
NSObject - 需要swizzle的方法必须有动态属性
dynamic
- 包含swizzle方法的类,需继承自
class Father: NSObject {
@objc dynamic func makeMoney() {
print("make money")
}
}
extension Father {
static func swizzle() {
let originSelector = #selector(Father.makeMoney)
let swizzleSelector = #selector(Father.swizzle_makeMoney)
let originMethod = class_getInstanceMethod(Father.self, originSelector)
let swizzleMethod = class_getInstanceMethod(Father.self, swizzleSelector)
method_exchangeImplementations(originMethod!, swizzleMethod!)
}
@objc func swizzle_makeMoney() {
print("have a rest and make money")
}
}
Father.swizzle()
var tmp = Father()
tmp.makeMoney() // have a rest and make money
tmp.swizzle_makeMoney() // make money
- 使用
@_dynamicReplacement(for: )实现class Father { dynamic func makeMoney() { print("make money") } } extension Father { @_dynamicReplacement(for: makeMoney()) func swizzle_makeMoney() { print("have a rest and make money") } } Father().makeMoney() // have a rest and make money
实现原理
method swizzling都是利用runtime来获取selector的实现,来动态改变。Swift通过OC的runtime特性来实现。
在Swift中使用dynamic属性来标记方法,使该方法只在运行期编译,以达到动态派发
References
- Objective-C-Method-Swizzling
- Apple: using objective-c runtime features in Swift
- Swift 5 之后 "Method Swizzling"?: 介绍了
@_dynamicReplacement(for: )和连环Hook的场景