复制多个方法swizzles的方法IMP

时间:2012-02-11 17:58:20

标签: objective-c ios objective-c-runtime swizzling

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

这现在可以正常工作,但我一次只能使用一种方法。问题似乎是,一旦我调整第一个方法,我的IMP捕获和转发方法现在已经与其他方法IMP交换。由于他们使用新交换的IMP来替换其他IMP,所以进一步尝试这个搞砸了。

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

2)我将MethodA与CustomCatchAllMEthod交换。 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:));

2 个答案:

答案 0 :(得分:3)

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

要正确使用,你必须使用:

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

这会给你留下一个问题:如何转发到原始实现?
这就是原始模式使用exchangeImplementations的原因。

在你的情况下你可以:

  • 保留原始IMP周围的表格或
  • 使用一些常用前缀重命名原始方法,以便您可以从catchAll:
  • 构建对它们的调用

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

答案 1 :(得分: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);