Скопируйте метод IMP для нескольких методов Swizzles

У меня есть настроенный класс, который в идеале будет читать методы любого переданного класса, а затем отображать все из них на один селектор во время выполнения, прежде чем перенаправлять их в свой оригинальный селектор.

Это работает прямо сейчас, но я могу сделать это только для одного метода за раз. Похоже, проблема заключается в том, что после того, как я перехитрил первый метод, мой IMP для перехвата и пересылки метода теперь поменялся местами с другими методами IMP. Любые дальнейшие попытки облажаться, потому что они используют недавно замененный IMP, чтобы заменить другие.

1) Итак, у меня есть MethodA, MethodB и CustomCatchAllMethod.

2) Я меняю метод A с CustomCatchAllMEthod. MethodA-> CustomCatchAllMethod, CustomCatchAllMethod-> MethodA

3) Теперь я пытаюсь переключиться на MethodB с CustomCatchAllMethod, но так как 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 ответа

Решение

Этот распространенный метод переключения методов работает только тогда, когда вы хотите перехватить один метод другим. В вашем случае вы в основном перемещаете реализацию для catchAll: вокруг вместо того, чтобы вставлять его везде.

Для этого вам необходимо использовать:

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

Это оставляет вам одну проблему: как перейти к первоначальной реализации?
Это то, что использовал оригинальный шаблон exchangeImplementations за.

В вашем случае вы могли бы:

  • вести таблицу оригинала IMPвокруг или
  • переименуйте исходные методы с некоторым общим префиксом, чтобы вы могли создавать вызовы из них catchAll:

Обратите внимание, что вы можете обрабатывать методы одинаковой арности, только если вы хотите переслать все через один и тот же метод

Вы можете захватить оригинальный 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);
Другие вопросы по тегам