Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Override a method via ObjC Category and call the default implementation?

Tags:

objective-c

When using categories, you can override implementation methods with your own like so:

// Base Class 
@interface ClassA : NSObject 
- (NSString *) myMethod;
@end
@implementation ClassA
- (NSString*) myMethod { return @"A"; }
@end

//Category
@interface ClassA (CategoryB) 
- (NSString *) myMethod;
@end
@implementation ClassA (CategoryB)
- (NSString*) myMethod { return @"B"; }
@end

Calling the method "myMethod" after including the category nets the result "B".

What is the easiest way for the Category implementation of myMethod to call the original Class A myMethod? As near as I can figure out, you'd have to use the low level calls to get the original method hook for Class A and call that, but it seemed like there would be a syntactically easier way to do this.

like image 872
Kendall Helmstetter Gelner Avatar asked Jul 06 '09 04:07

Kendall Helmstetter Gelner


4 Answers

If you want a hackish way to do this that involves mucking with the objective-c runtime you can always use method swizzling (insert standard disclaimers here.) It will allow you to store the different methods as arbitrariliy named selectors, then swap them in at runtime as you need them.

like image 158
cobbal Avatar answered Nov 20 '22 07:11

cobbal


From comp.lang.objective-C FAQ listing: "What if multiple categories implement the same method? Then the fabric of the Universe as we know it ceases to exist. Actually, that's not quite true, but certainly some problems will be caused. When a category implements a method which has already appeared in a class (whether through another category, or the class' primary @implementation), that category's definition overwrites the definition which was previously present. The original definition can no longer be reached by the Objective-C code. Note that if two categories overwrite the same method then whichever was loaded last "wins", which may not be possible to predict before the code is launched."

From developer.apple.com: "When a category overrides an inherited method, the method in the category can, as usual, invoke the inherited implementation via a message to super. However, if a category overrides a method that already existed in the category's class, there is no way to invoke the original implementation"

like image 29
Oren Trutner Avatar answered Nov 20 '22 06:11

Oren Trutner


Check out my article about a solution found on the Mac Developer Library: http://codeshaker.blogspot.com/2012/01/calling-original-overridden-method-from.html

Basically, it's the same as the above Method Swizzling with a brief example:

#import <objc/runtime.h>

@implementation Test (Logging)

- (NSUInteger)logLength {
    NSUInteger length = [self logLength];
    NSLog(@"Logging: %d", length);
    return length;
}

+ (void)load {
    method_exchangeImplementations(class_getInstanceMethod(self, @selector(length)), class_getInstanceMethod(self, @selector(logLength)));
}

@end
like image 12
Andras Avatar answered Nov 20 '22 06:11

Andras


With the swizzling "helper" methods included in ConciseKit, you actually call the default implementation… weirdly enough.. by calling your SWIZZLED implementation..

You set it up in + (void) load, calling + (BOOL)swizzleMethod:(SEL)originalSelector with:(SEL)anotherSelector in:(Class)klass;, i.e.

[$ swizzleMethod:@selector(oldTired:) 
            with:@selector(swizzledHotness:) in:self.class];

and then in the swizzled method.. let's suppose it returns -(id).. you can do your mischief, or whatever reason you are swizzling in the first place… and then, instead of returning an object, or self, or whatnot..

return [self swizzledHotness:yourSwizzledMethodsArgument];

As explained here…

In this method, it looks like we're calling the same method again, causing and endless recursion. But by the time this line is reached the two method have been swapped. So when we call swizzled_synchronize we're actually calling the original method.

It feels and looks odd, but.. it works. This enables you to add endless embellishments to existing methods, and still "call super" (actually self) and reap the benefits of the original method's handiwork… even without access to the original source.

like image 1
Alex Gray Avatar answered Nov 20 '22 06:11

Alex Gray