Reputation: 21726
I use a specific API, which has some class, for example ClassA
.
ClassA
has a property importantProperty
, ivar = _importantProperty
, setter = setImportantProperty
What I need is actually to handle in debug when is property is changed, and what values are set to this property and print the stacktrace.
I can not inherit this class and override this method in my case, because it has defined class.
So I created a debug category, which overrides setImportantProperty
method and now I can handle the stacktrace and values changes, but this method makes original method unreachable, and ivar value can not be changed. Is there any way to change this ivar?
Here is my method:
@implementation ClassA (Test)
-(void) setImportantProperty:(id) newValue {
NSLog(@"%@, %@", newValue, [NSThread callStackSymbols]);
}
@end
So the question is there any way to implement such code _importantProperty = newValue
in my method or are there any other ways to use in my case?
Thanks in advance!
Upvotes: 1
Views: 490
Reputation: 7764
As @vikingosegundo suggested, you could use method swizzling:
#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
Here we declare a new method swizzled_setImportantProperty:
, and at runtime we swap it's implementation with setImportantProperty:
implementation. So that when we call setImportantProperty:
in code, the implementation for swizzled_setImportantProperty
will be called and vice versa.
That's why when we call swizzled_setImportantProperty:
inside the swizzled_setImportantProperty:
implementation, it won't call an endless recursion, because setImportantProperty:
implementation will be called. Just what we need.
UPDATE: Since overriding a +load
method might cause problems if it have already been implemented (or if might be implemented in the future by the library creators), there is a better option suggested by @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);
}
Upvotes: 1