Вызов супер без ключевого слова "супер"
Я хочу "реализовать" Исправить и продолжить функциональность ", которая была в 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