Скопируйте метод 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);