3

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

上下文

主要思想是:当我需要“快速修复某些东西”时,我不会重新编译项目。我正在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 class

更新:添加了替换实现代码。

4

4 回答 4

5

你不得不

  1. 获取接收者类(例如 with object_getClass(rcv)
  2. 然后得到它的超类(用class_getSuperclass(class)
  3. 然后得到它的实现(用class_getMethodImplementation(superclass, sel)
  4. 然后打电话给小鬼。

完毕

如果您得到 nil 或 NULL,请在任何步骤停止。

哦,这一切似乎很愚蠢。但我认为这个问题只是缺乏背景来了解这种黑客攻击的动机。

[更新]

对未来读者的解释:

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

于 2012-04-26T09:28:01.837 回答
0

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

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

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

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

于 2012-04-26T07:34:11.477 回答
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];
}
于 2012-05-10T19:08:46.810 回答
-2

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

于 2012-04-26T07:29:34.567 回答