48

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.

Kendall Helmstetter Gelner
  • 74,769
  • 26
  • 128
  • 150

4 Answers4

37

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.

Michael Dautermann
  • 88,797
  • 17
  • 166
  • 215
cobbal
  • 69,903
  • 20
  • 143
  • 156
  • 11
    It's not necessarily a 'hackish' way. The Objective-C runtime is there for a reason. It is what makes Objective-C superior to other compiled languages. – Alexei Sholik Jun 01 '11 at 11:10
  • Based on my experience, cutting to the chase, you want to use JRSwizzle, as mentioned in the above link. It's been bulletproof for me. – Clay Bridges Jul 12 '11 at 14:48
  • 3
    Link to JRSwizzle code: https://github.com/rentzsch/jrswizzle … I've come to love this, method swizzling is VERY powerful whenever you want to tweak a 3rd party library to do as you please! – CodeSmile Dec 11 '11 at 15:58
20

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"

Oren Trutner
  • 23,752
  • 8
  • 54
  • 55
12

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
Andras
  • 366
  • 3
  • 6
1

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.

Alex Gray
  • 16,007
  • 9
  • 96
  • 118