1

などのクラスを持つ特定の API を使用しますClassA

ClassAプロパティimportantPropertyivar = _importantProperty、セッター =を持っていますsetImportantProperty

私が必要とするのは、プロパティが変更されたときに実際にデバッグで処理し、このプロパティに設定されている値とスタックトレースを出力することです。

クラスが定義されているため、このクラスを継承してこのメ​​ソッドをオーバーライドすることはできません。

そのため、メソッドをオーバーライドするデバッグ カテゴリを作成しsetImportantProperty、スタック トレースと値の変更を処理できるようになりましたが、このメソッドにより元のメソッドに到達できなくなり、ivar 値を変更できなくなります。この ivar を変更する方法はありますか?

これが私の方法です:

@implementation ClassA (Test)

-(void) setImportantProperty:(id) newValue {

    NSLog(@"%@, %@", newValue, [NSThread callStackSymbols]);
}

@end

それで、質問は_importantProperty = newValue私の方法でそのようなコードを実装する方法はありますか、それとも私の場合に使用する他の方法はありますか?

前もって感謝します!

4

1 に答える 1

1

@vikingosegundo が示唆したように、メソッドのスウィズリングを使用できます。

#import "ClassA+Test.h"
#import <objc/runtime.h> // Needed for method swizzling

@implementation ClassA(Test)

-(void) swizzled_setImportantProperty:(id) newValue {
    [self swizzled_setImportantProperty: newValue]; //it is NOT an endless recursion.
    NSLog(@"%@, %@", newValue, [NSThread callStackSymbols]);
}

+(void)load
{
    Method original, swizzled;
    original = class_getInstanceMethod(self, @selector(setImportantProperty:));
    swizzled = class_getInstanceMethod(self, @selector(swizzled_setImportantProperty:)); //UPDATE: missed a column here, sorry!
    method_exchangeImplementations(original, swizzled);
}

@end

ここで新しいメソッドを宣言し、swizzled_setImportantProperty:実行時にその実装を実装と交換しますsetImportantProperty:setImportantProperty:そのため、コードを呼び出すと、の実装swizzled_setImportantPropertyが呼び出され、その逆も同様です。

そのためswizzled_setImportantProperty:、実装内で呼び出すと、実装が呼び出されるswizzled_setImportantProperty:ため、無限再帰は呼び出されませんsetImportantProperty:。必要なものだけです。

更新:+loadメソッドが既に実装されている場合(またはライブラリ作成者によって将来実装される可能性がある場合)、メソッドをオーバーライドすると問題が発生する可能性があるため、 @ JoshCaswell によって提案されたより良いオプションがあります:

#import "ClassA+Test.h"
#import <objc/runtime.h> // Needed for method swizzling

@implementation ClassA(Test)

-(void) swizzled_setImportantProperty:(id) newValue {
    [self swizzled_setImportantProperty: newValue]; //it is NOT an endless recursion.
    NSLog(@"%@, %@", newValue, [NSThread callStackSymbols]);
}

@end

void swizzleSetImportantProperty(void) __attribute__((constructor))
{
    Method original, swizzled;
    original = class_getInstanceMethod([ClassA class], @selector(setImportantProperty:));
    swizzled = class_getInstanceMethod([ClassA class], @selector(swizzled_setImportantProperty:)); //UPDATE: missed a column here, sorry!
    method_exchangeImplementations(original, swizzled);
}
于 2013-12-25T18:56:02.760 に答える