Как мне реализовать метод Swizzling? - PullRequest
9 голосов
/ 21 марта 2011

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

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

Спасибо!

Ответы [ 2 ]

33 голосов
/ 21 марта 2011

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

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

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

Похоже, что он будет рекурсивно вызывать себя, но не будет, потому что мы собираемся поменять местами, вызывая * 1006.* вызывает этот метод при вызове 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, чтобы просто поменять местами новые и старые версии.
6 голосов
/ 02 июля 2013

Библиотека 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
...