原理
交换两个方法的引用,就是调用a方法,实际执行b方法
func method_exchangeImplementations(_ m1: Method, _ m2: Method)
代码实现
+ (BOOL)jr_swizzleMethod:(SEL)origSel_ withMethod:(SEL)altSel_ error:(NSError * *)error_ {
Method origMethod = class_getInstanceMethod(self, origSel_);
if (! origMethod) {
SetNSError(error_, @"original method %@ not found for class %@", NSStringFromSelector(origSel_), [self class]);
return NO;
}
Method altMethod = class_getInstanceMethod(self, altSel_);
if (! altMethod) {
SetNSError(error_, @"alternate method %@ not found for class %@", NSStringFromSelector(altSel_), [self class]);
return NO;
}
class_addMethod(self,
origSel_,
class_getMethodImplementation(self, origSel_),
method_getTypeEncoding(origMethod));
class_addMethod(self,
altSel_,
class_getMethodImplementation(self, altSel_),
method_getTypeEncoding(altMethod));
method_exchangeImplementations(class_getInstanceMethod(self, origSel_), class_getInstanceMethod(self, altSel_));
return YES;
第三方库
安全隐患
- 只在 + load 中执行 swizzling 才是安全的。 命名如果冲突将导致之前 hook 的失效 或者是循环调用。
说的是通常的 MethodSwizzling 是在分类里面实现的, 而分类的 Method 是被 Runtime 加载的时候追加到类的 MethodList ,如果不是在 + load 是执行的 Swizzling 一旦出现重名,那么 SEL 和 IMP 不匹配致 hook 的结果是循环调用。
- 被 hook 的方法必须是当前类自身的方法,如果把继承来的 IMP copy 到自身上面会存在问题。父类的方法应该在调用的时候使用,而不是 swizzling 的时候 copy 到子类。
父类的分类加载不一定在子类之前
- 被 Swizzled 的方法如果依赖与 cmd ,hook 之后 cmd 发送了变化,就会有问题(一般你 hook 的是系统类,也不知道系统用没用 cmd 这个参数)。
Objective-C Method 都会有两个隐含的参数 self, cmd,有的时候开发者在使用关联属性的适合可能懒得声明 (void *) 的 key,直接使用 cmd 变量 objc_setAssociatedObject(self, _cmd, xx, 0); 这会导致对当前 IMP 对 cmd 的依赖.如果系统方法中也使用了_cmd,那么将会导致关联错乱。
网友评论