Скопируйте метод IMP для нескольких методов Swizzles - PullRequest
4 голосов
/ 11 февраля 2012

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

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

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

2) Я обмениваю MethodA с 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 ]

3 голосов
/ 11 февраля 2012

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

Для этого вам нужно использовать:

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

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

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

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

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

0 голосов
/ 26 февраля 2014

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