1

我使用一个特定的 API,它有一些类,例如ClassA.

ClassA有一个属性importantProperty,ivar = _importantProperty,setter =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:代码时,就会调用 for 的实现,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 回答