Как реализовать смешение методов?

Я пытаюсь изменить поведение программы (у меня нет ее источника), используя SIMBL. Я использовал дамп класса и обнаружил, что мне нужно переопределить метод экземпляра

Этот метод находится в классе под названием контроллер. Все, что мне нужно сделать, это получить аргумент arg1 и все. Может быть, NSLog или опубликовать уведомление ... Я читал о методе swizzling в Objective-C, но как я могу его использовать? Мне нужно было бы обратиться к классу MessageController, курса которого у меня нет.

Спасибо!


person user635064    schedule 20.03.2011    source источник
comment
На этом пути лежит безумие... не делай этого!   -  person bbum    schedule 21.03.2011
comment
Похоже на дубликат stackoverflow.com/questions/4152907/simbl-with -method-swizzling   -  person danyowdee    schedule 21.03.2011


Ответы (2)


Я предполагаю, что вам нужно вызвать исходную реализацию после выполнения вашего NSLog; если нет, вы можете просто использовать категорию в классе, чтобы переопределить метод.

Чтобы прокачать метод, сначала вам нужен метод замены. Обычно я помещаю что-то вроде этого в категорию целевого класса:

- (void)replacementReceiveMessage:(const struct BInstantMessage *)arg1 {
    NSLog(@"arg1 is %@", arg1);
    [self replacementReceiveMessage:arg1];
}

Похоже, что он будет рекурсивно вызывать сам себя, но этого не произойдет, потому что мы собираемся поменять местами, поэтому вызов ReceiveMessage: вызывает этот метод, а вызов replacementReceiveMessage: вызывает старую версию.

Второй шаг — использовать функции времени выполнения для фактического выполнения свопа. Преимущество использования категории в том, что вы можете использовать load в категории для выполнения работы:

+ (void)load {
    SEL originalSelector = @selector(ReceiveMessage:);
    SEL overrideSelector = @selector(replacementReceiveMessage:);
    Method originalMethod = class_getInstanceMethod(self, originalSelector);
    Method overrideMethod = class_getInstanceMethod(self, overrideSelector);
    if (class_addMethod(self, originalSelector, method_getImplementation(overrideMethod), method_getTypeEncoding(overrideMethod))) {
            class_replaceMethod(self, overrideSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
    } else {
            method_exchangeImplementations(originalMethod, overrideMethod);
    }
}

Есть два случая, которые необходимо обработать:

  • Если метод, который мы изучаем, на самом деле определен в суперклассе, мы должны использовать class_addMethod, чтобы добавить реализацию ReceiveMessage: в целевой класс, что мы и делаем, используя нашу замену реализации. Затем мы можем использовать class_replaceMethod для замены replacementReceiveMessage: реализацией суперкласса, чтобы наша новая версия могла корректно вызывать старую.
  • Если метод определен в целевом классе, class_addMethod потерпит неудачу, но тогда мы можем использовать method_exchangeImplementations, чтобы просто поменять местами новую и старую версии.
person Anomie    schedule 20.03.2011
comment
#импорт ‹objc/runtime.h› #импорт ‹objc/message.h› - person bbrame; 11.03.2014
comment
Если у него нет исходного кода, каковы детали, чтобы на самом деле скомпилировать и внедрить этот код в программу? - person alecail; 19.02.2015

За это отвечает библиотека jrswizzle. Делать это самостоятельно не рекомендуется, потому что нужно очень много деталей. (См. таблицу с ошибками предыдущих реализаций в файле readme jrswizzle.)

Скажем, у вас есть такой класс:

@interface Weh : NSObject
-(void)foo;
-(void)bar;
@end

@implementation Weh
-(void)foo {
    NSLog(@"Foo called");
}
-(void)bar {
    NSLog(@"Bar called");
    [self bar];
}
@end

Вы можете использовать его следующим образом:

Weh *weh = Weh.new;
[weh foo];
[Weh jr_swizzleMethod:@selector(foo) withMethod:@selector(bar) error:nil];
[weh foo]; 

Вывод:

Foo called
Bar called
Foo called
person Isak    schedule 02.07.2013
comment
Один вопрос .... Не вызовет ли вызов [self bar]; внутри -(void)bar цикл? - person TheTiger; 18.05.2016
comment
@TheTiger Нет, потому что реализация bar, как написано в Weh, была заменена foo. Во время выполнения реализация foo печатает Bar и вызывает bar, тогда как реализация bar просто печатает Foo. Вот почему после swizzle [weh foo] печатает Bar, а затем Foo. Если бы исходная реализация foo вызывала foo, то [weh foo] зацикливалась бы бесконечно, печатая Bar, затем Foo, а затем Bar до бесконечности. - person BallpointBen; 21.06.2017