复制方法 IMP 以进行多个方法混合

Jac*_*man 5 objective-c objective-c-runtime swizzling ios

我设置了一个类,理想情况下将读取传入的任何类的方法,然后在运行时将它们全部映射到单个选择器,然后将它们转发到原始选择器。

这现在确实有效,但我一次只能使用一种方法。问题似乎是,一旦我调整第一个方法,我用于捕获和转发该方法的 IMP 现在已与其他方法 IMP 交换。任何进一步的尝试都会搞砸,因为他们使用新交换的 IMP 来替换其他 IMP。

1)所以我有MethodA、MethodB和CustomCatchAllMethod。

2)我将 MethodA 与 CustomCatchAllMethod 交换。方法A->自定义CatchAll方法,自定义CatchAll方法->方法A

3)现在我尝试用CustomCatchAllMethod交换到MethodB,但是由于CustomCatchAllMethod现在= MethodA,MethodB变成MethodA和MethodA->MethodB。

那么,如何为每个我想要拦截的新选择器获取/复制 IMP 的新实例呢?

这是上述流程的粗略模型:

void swizzle(Class classImCopying, SEL orig){
 SEL new = @selector(catchAll:);
 Method origMethod = class_getInstanceMethod(classImCopying, orig);
 Method newMethod = class_getInstanceMethod(catchAllClass,new);
 method_exchangeImplementations(origMethod, newMethod);
}

//In some method elsewhere

//I want this to make both methodA and methodB point to catchAll:
swizzle(someClass, @selector(methodA:));
swizzle(someClass, @selector(methodB:));
Run Code Online (Sandbox Code Playgroud)

Geo*_*che 3

这种常见的方法调配模式仅在您想要拦截一种方法与另一种方法时才有效。在您的情况下,您基本上是在四处移动实现,catchAll:而不是将其插入到任何地方。

为了正确地做到这一点,你必须使用:

IMP imp = method_getImplementation(newMethod);
method_setImplementation(origMethod, imp);
Run Code Online (Sandbox Code Playgroud)

但这给您留下了一个问题:如何转发到原始实现?
这就是原始图案的用途exchangeImplementations

在你的情况下,你可以:

  • 保留一张原件的桌子IMP,或者
  • 使用一些通用前缀重命名原始方法,以便您可以从以下位置构建对它们的调用catchAll:

请注意,当您想通过相同的方法转发所有内容时,只能处理相同数量的方法。