4

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

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

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

2)我用 CustomCatchAllMEthod 交换 MethodA。MethodA->CustomCatchAllMethod、CustomCatchAllMethod->MethodA

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:));
4

2 回答 2

3

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

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

IMP imp = method_getImplementation(newMethod);
method_setImplementation(origMethod, imp);

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

在您的情况下,您可以:

  • 在周围保留一张原始IMPs 的表格或
  • 使用一些公共前缀重命名原始方法,以便您可以从catchAll:

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

于 2012-02-11T18:12:58.327 回答
0

您可以使用块捕获原始 IMP,获取块的 IMP 并将其设置为方法的实现。

Method method = class_getInstanceMethod(class, setterSelector);
SEL selector = method_getName(method);
IMP originalImp = method_getImplementation(method);

id(^block)(id self, id arg) = ^id(id self, id arg) {
    return ((id(*)(id, SEL, id))originalImp)(self, selector, arg);
};

IMP newImp = imp_implementationWithBlock(block);
method_setImplementation(method, newImp);
于 2014-02-25T21:28:36.407 回答