从类别中设置ivar值

时间:2013-12-25 18:33:28

标签: objective-c categories ivar

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

ClassA有一个属性importantProperty,ivar = _importantProperty,setter = setImportantProperty

我需要的是在属性更改时实际处理调试,以及为此属性设置了什么值并打印堆栈跟踪。

我无法继承此类并在我的情况下覆盖此方法,因为它已定义了类。

所以我创建了一个调试类别,它覆盖了setImportantProperty方法,现在我可以处理堆栈跟踪和值更改,但是这种方法使得原始方法无法访问,并且无法更改ivar值。有没有办法改变这个伊娃?

这是我的方法:

@implementation ClassA (Test)

-(void) setImportantProperty:(id) newValue {

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

@end

所以问题是在我的方法中有没有办法实现这样的代码_importantProperty = newValue,还是有其他方法可以用在我的案例中?

提前致谢!

1 个答案:

答案 0 :(得分: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);
}