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

发布于 2025-01-04 20:07:43 字数 1021 浏览 4 评论 0原文

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

这现在确实有效,但我一次只能使用一种方法。问题似乎是,一旦我调整第一个方法,我用于捕获和转发该方法的 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:));

I have a class set up that ideally will read the methods of any class passed in and then map all of them to on single selector at runtime before forwarding them off to their original selector.

This does work right now, but I can only do it for one method at a time. The issue seems to be that once I swizzle the first method, my IMP to catch and forward the method has now been swapped with that other methods IMP. Any further attempts at this screw up because they use newly swapped IMP to replace the others.

1)So I have MethodA, MethodB, and CustomCatchAllMethod.

2)I swap MethodA with CustomCatchAllMEthod. MethodA->CustomCatchAllMethod, CustomCatchAllMethod->MethodA

3)Now I try to swap to MethodB with CustomCatchAllMethod as well, but since CustomCatchAllMethod now = MethodA, MethodB becomes MethodA and MethodA->MethodB.

So how do I get/copy a new instance of my IMP for each new selector I want to intercept?

Here's a rough mockup of the above flow:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

眸中客 2025-01-11 20:07:43

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

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

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

但这给你带来了一个问题:如何转发到原始实现?
这就是原始模式使用 exchangeImplementations 的目的。

在您的情况下,您可以:

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

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

That common method-swizzling pattern only works when you want to intercept one method with one other. In your case you are basically moving the implementation for catchAll: around instead of inserting it everywhere.

To properly to this you'd have to use:

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

This leaves you with one problem though: how to forward to the original implementation?
That is what the original pattern used exchangeImplementations for.

In your case you could:

  • keep a table of the original IMPs around or
  • rename the original methods with some common prefix, so you can build a call to them from catchAll:

Note that you can only handle methods of the same arity when you want to forward everything through the same method.

你在看孤独的风景 2025-01-11 20:07:43

您可以使用块捕获原始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);

You can capture original IMP with block, get block's IMP and set it as implementation of method.

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);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文