Вызов супер без ключевого слова "супер"

Я хочу "реализовать" Исправить и продолжить функциональность ", которая была в Xcode 3.

КОНТЕКСТ

Основная идея: Когда мне нужно "Fix something fast", я не перекомпилирую проект. Я компилирую небольшой Attacker class с помощью "обновленной" реализации метода, загружая его в память и заменяя метод VictimClass, который имеет реализацию incorrect во время выполнения. Я думаю, что этот метод будет работать быстрее, чем полная перекомпиляция проекта.

Когда я закончил с исправлениями, я просто копирую источник метода Attacker class в Victim class.

ПРОБЛЕМА

В настоящий момент я не знаю, как правильно вызвать [super ...] в классе Attacker.

Например, у меня есть VictimClass

@interface VictimClass : UIView @end
@implementation VictimClass
- (void)drawRect:(CGRect)rect {
  [super drawRect:rect];
}
@end


@interface AttackerClass : NSObject @end
@implementation AttackerClass 
- (void)drawRect:(CGRect)rect {
  [super drawRect:rect];
  [self setupPrettyBackground];
}
@end
....

// EXCHANGE IMPLEMENTATIONS
Method m = class_getInstanceMethod([AttackerClass class], @selector(drawRect:));
const char * types = method_getTypeEncoding(m);
IMP attackerImp = method_getImplementation(m);

class_replaceMethod([VictimClass class], @selector(drawRect:), attackerImp, types);


// Invoking drawRect on Victim
VictimClass * view = /* */;
[view setNeedsDisplay];

В этот момент, когда будет вызываться метод drawRect:, это приведет к исключению, так как drawRect: будет вызван в классе NSObject, но не в классе UIView

Итак, мой вопрос: как правильно называть [super drawRect:] в AttackerClass, чтобы иметь возможность правильно обменивать реализацию во время выполнения?

Основная идея - предоставить способ правильно заменить любой метод класса Victim методом класса Attacker. Как правило, вы не знаете, суперкласс Victim class.

UPDATE: добавлена ​​замена кода реализации.

Ответы

Ответ 1

Вам нужно

  • получить класс приемников (например, с помощью object_getClass(rcv))
  • тогда получим суперкласс (с class_getSuperclass(class))
  • тогда получим его реализацию (с class_getMethodImplementation(superclass, sel))
  • затем вызовите imp.

сделать

Остановитесь на любом шаге, если у вас есть нуль или NULL.

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

[Обновление]

Объяснение для будущих читателей:

Ключевое слово super разрешено во время компиляции. Поэтому при изменении методов во время выполнения он не предназначен. Метод, который должен быть инъецирован в некоторый объект (и его иерархию классов) во время выполнения, должен выполнять супервызов через среду выполнения, как описано выше.

Ответ 2

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

@implementation AttackerClass 
-(void) drawRect:(CGRect)rect
{
    if( [super respondsToSelector:@selector(drawRect:)] )
    {
        [super drawRect:rect];
    }
    [self setupPrettyBackground];
}
@end

Это проверит, знает ли суперкласс "знает о" drawRect: и не вызывает его в случае, если super не имеет селектора drawRect:.

Следовательно, когда суперкласс < <24 > , сообщение drawRect: не будет отправлено. Когда вы меняете его на UIView во время выполнения (независимо от вашей причины), сообщение можно безопасно отправить.

Ответ 3

Один из подходов - использование objc_msgSendSuper. Ваш метод - [AttackerClass drawRect:] будет иметь следующую реализацию:

- (void)drawRect:(CGRect)rect {
  struct objc_super superTarget;
  superTarget.receiver = self;
  superTarget.class = object_getClass(self);
  objc_msgSendSuper(&superTarget, @selector(drawRect:), rect);
  [self setupPrettyBackground];
}

Ответ 4

но почему вам нужно вызвать метод draw rect для суперкласса NSObject, когда NSObject не получил этот метод? просто не делайте этого... назовите его просто в drawimClass drawrect