Как правильно выпить с method_setImplementation()?

Я пытаюсь понять, как правильно пить, используя method_setImplementation(), До сих пор в Интернете я видел два преимущественно популярных метода метода Swizzle, один раз использующий method_setImplementation() в то время как другие levarages method_exchangeImplementation,

При использовании method_exchangeImplementation() Вы можете столкнуться с проблемой, когда целевой класс не реализует метод, который вы пытаетесь использовать. Во избежание проблем с классом-предком, реализующим метод и неправильно использующим его, вы сначала добавляете реализацию к своему целевому классу, а затем меняете swizzledMethod IMP с вашим методом классов предков, чтобы навести порядок. Это все имеет смысл для меня...

Мой вопрос заключается в том, как мы справляемся с тем же вышеупомянутым случаем, когда method_setImplementation? Разве мы не должны обращаться с этим делом (интуитивно я чувствую, что мы делаем)? Если нам нужно обработать приведенный выше пример кода, это очень мне поможет, потому что я заблудился в том, как его обрабатывать.

1 ответ

Решение

При работе со свободными функциями (то есть обычными функциями) необходимо учитывать фактическую сигнатуру метода, Objective-C компилятор генерирует для метода, который вы хотите swizzled, и тот факт, что IMP это просто указатель на функцию, объявленный так:

typedef id (*IMP)(id, SEL, ...);

Предполагая, что у вас есть метод с именем doSomethingWithObject:afterDelay:который выглядит так

- (void)doSomethingWithObject:(id)object afterDelay:(NSTimeInterval)delay

соответствующая функция C, которую генерирует компилятор, - это (см. Википедию для получения дополнительной информации о календаре в Objective-C):

void _i_MyClass_doSometingWithObject_afterDelay(id self, SEL _cmd, id object, NSTimeInterval delay)

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

void swizzledDoSometingWithObjectAfterDelay(id self, SEL _cmd, id object, NSTimeInterval delay) {
    // custom implementation
}

// ....
method_setImplementation(method, (IMP)swizzledDoSometingWithObjectAfterDelay);

Если метод, который вы хотите swizzle, может быть объявлен в классе-предке, и вы хотите использовать его только для интересующего вас дочернего класса, вы можете использовать class_replaceMethod (), который либо добавляет, либо заменяет метод, и это только влияет на целевой класс:

SEL selector = @selector(doSomethingWithObject:afterDelay:);
IMP newImp = (IMP)swizzledDoSometingWithObjectAfterDelay
Method method = class_getClassMethod([MyClass class], selector);
const char * encoding = method_getTypeEncoding(method);
class_replaceMethod([MyClass class], selector, newIMP, encoding)
Другие вопросы по тегам