不使用“超级”关键字调用超级

时间:2012-04-26 07:22:33

标签: ios objective-c iphone objective-c-runtime

我想要实现Xcode 3中的“修复和继续功能”。

CONTEXT

主要思想是:   当我需要“快速修复某些东西”时,我不会重新编译,投射。我正在使用'updated'方法实现编译小Attacker class,将其加载到内存中并替换在运行时具有incorrect实现的VictimClass方法。 我认为这个方法可以更快地完成整个项目的重新编译。

当我完成修复后,我只是将Attacker class方法的来源复制到Victim class

问题

目前,我不知道在攻击者类中如何正确地调用[super ...]

例如,我有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类上调用

所以,我的问题是,如何在AttackerClass中正确调用[super drawRect:],以便有可能在运行时正确地交换实现?

主要思想是提供一种方法,通过Attacker的类方法正确替换Victim类中的任何方法。通常,您不知道Victim class的超类。

更新:替换添加的实施代码。

4 个答案:

答案 0 :(得分:5)

你必须

  1. 获取接收器类(例如使用object_getClass(rcv)
  2. 然后获取它的超类(使用class_getSuperclass(class)
  3. 然后获得它的实现(使用class_getMethodImplementation(superclass, sel)
  4. 然后调用imp。
  5. 完成

    如果你有nil或NULL,请在任何步骤停止。

    哦,这一切看起来很傻。但我认为这个问题只是缺乏上下文才能看到这种黑客攻击的动机。

    <强> [更新]

    对未来读者的解释:

    super关键字在编译时解析。因此,在运行时更改方法时,它不是预期的事情。如上所述,在运行时注入某个对象(及其类层次结构)的方法必须通过运行时进行超级调用。

答案 1 :(得分:0)

假设运行时更改涉及修改超类,您必须执行以下操作:

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

这将检查超类是否“知道”drawRect:,并且在super没有drawRect:选择器的情况下不会调用它。

因此,当超类为NSObject时,将不会发送drawRect:消息。当您在运行时将其更改为UIView时(无论您的原因是什么),都可以安全地发送消息。

答案 2 :(得分:0)

一种方法是使用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];
}

答案 3 :(得分:-2)

但是当NSObject没有那个方法时,你为什么需要为超类NSObject调用draw rect方法呢?只是不要这样做......只在VictimClass中直接调用