3

Xcode 3にあった「Fix and continue機能」を実装したい。

コンテキスト:

主なアイデアは次のとおりです。「何かをすばやく修正する」必要がある場合、プロジェクトを再コンパイルするのではありません。Attacker class「更新された」メソッド実装で小さくコンパイルし、それをメモリにロードしincorrect、実行時に実装されている VictimClass のメソッドを置き換えています。この方法は、プロジェクト全体を再コンパイルするよりも速く機能すると思います。

修正が完了したら、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];

この時点で、メソッドが呼び出されると、 UIView クラスではなく NSObject クラスで呼び出されるdrawRect:ため、例外が発生します。drawRect:

それで、私の質問は、[super drawRect:]実行時に実装を正しく交換できるようにするために、どのように AttackerClass を正しく呼び出すかということです。

主なアイデアは、Victim クラスの任意のメソッドを攻撃者のクラス メソッドに正しく置き換える方法を提供することです。一般に、 のスーパークラスはわかりませんVictim class

更新: 実装コードの置き換えが追加されました。

4

4 に答える 4

5

あなたはしなければならない

  1. 受信者クラスを取得します (例: でobject_getClass(rcv))
  2. それからスーパークラスを取得します(でclass_getSuperclass(class)
  3. 次に、それの実装を取得します(を使用class_getMethodImplementation(superclass, sel)
  4. 次に、インプを呼び出します。

終わり

nil または NULL を取得した場合は、任意のステップで停止します。

ああ、これはすべてばかげているようです。しかし、そのようなハッキングの動機を理解するには、この質問には文脈が欠けているだけだと思います。

[アップデート]

将来の読者のための説明:

キーワードはsuperコンパイル時に解決されます。したがって、実行時にメソッドを変更すると、意図したことにはなりません。実行時に何らかのオブジェクト (およびそのクラス階層) に注入されることを意図したメソッドは、上で概説したように、実行時にスーパー コールを実行する必要があります。

于 2012-04-26T09:28:01.837 に答える
0

1 つの方法は、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 に答える
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 に答える
-2

しかし、NSObject がそのメソッドを持っていないのに、スーパークラス NSObject の draw rect メソッドを呼び出す必要があるのはなぜですか? ただそれをしないでください... VictimClass drawrectで呼び出すだけです

于 2012-04-26T07:29:34.567 に答える