8

I need to override a method using a category. I'm also aware of the dangers with doing this (this is a private class inside another class and noone will ever write another overriding category method, so no undefined behaviour guaranteed). I've seen a lot of similar questions but they all address suppressing the compiler warning using something like this:

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"

// do your override

#pragma clang diagnostic pop

However this still leaves the linker warning. Is it possible to get rid of it for my particular override which I deem safe, in Xcode 4.6?

Here's a sample GitHub project illustrating the problem.

Community
  • 1
  • 1
lmirosevic
  • 15,787
  • 13
  • 70
  • 116
  • If you would exlain, why you need it, we could maybe offer you alternative solutions. your example code doesnt show us the reason, as it is not obvious in this example, why you dont use subclassing. – vikingosegundo Mar 08 '13 at 13:03
  • @vikingosegundo I want to replace a method implementation while still retaining access to its superclass (and find this a more elegant approach than method swizzling). See this question for more info: http://stackoverflow.com/questions/15291390/in-objective-c-how-do-you-entirely-replace-a-method-of-a-parent-class-while-pre In that question I've asked for alternative solutions to the problem. Here I'm just asking how to suppress the actual linker warnings for future reference. – lmirosevic Mar 08 '13 at 13:40
  • I never would consider suppressing a warning being elegant. Maybe it is less ugly than method swizzling… – vikingosegundo Mar 08 '13 at 13:52
  • 5
    Why do you think that no undefined behavior is guaranteed in your case? From [Avoid Category Method Name Clashes](http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.html#//apple_ref/doc/uid/TP40011210-CH6-SW4): in the Apple documentation: *"If the name of a method declared in a category is the same as a method in the original class, ..., the behavior is undefined as to which method implementation is used at runtime."* – Martin R Mar 09 '13 at 07:33
  • 1
    you can try method swizzle – Bryan Chen Mar 10 '13 at 00:42
  • Note that (referring to your sample project), the category method `-[Branch(stuff) init]` is called also if you call `[Branch new]`. So (apart from being undefined behavior as I think) the category method *globally* replaces the init method from the Branch class, it is not restricted to initializing FunkyBranch objects. – Martin R Mar 10 '13 at 09:33
  • Your questions needs context, after looking at all your references and understanding the original question I think I got it. Unfortunately what you want to do should not be done as @Martin R's link describes. I strongly recommend rethinking your approach and reading the [runtime documentation](http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ObjCRuntimeRef/Reference/reference.html) to understand why. In any case you are not the first one to run into this issue and there are many solutions that you can try ;) – Dario Apr 15 '13 at 00:56

1 Answers1

0

OK, as I explained in my comment what you are trying to do is dangerous and should not be done. I also suggested to read the runtime documentation to understand why and learn about other methods to achieve your goal. You should read that.

In any case an alternative to what you are doing, that produces the exact same results without raising so many red flags, is to use the runtime environment to "skip" one class in the initialization hierarchy, effectively "overriding" the superclass method.

Here's one option of how it's done, in your example project, change the FunkyBranch class implementation to this:

#import "FunkyBranch.h"
#import <objc/runtime.h>

typedef id(*InitIMP)(id,SEL);

@implementation FunkyBranch

-(id) init
{
    InitIMP superSuperInit = (InitIMP)class_getMethodImplementation([[self superclass] superclass], @selector(init));

    self = superSuperInit(self, @selector(init));
    if (self)
    {
        NSLog(@"FunkyBranch initialized");
    }
    return self;
}

@end

It will have the same results as your current implementation without the dangers of what you are doing.

Please keep in mind that casting the function pointer to the correct type is of utmost importance, also I still believe that you should rethink your approach instead of forcing the runtime to do something it's not designed to do. In any case, this answers your question.

Dario
  • 671
  • 5
  • 14
  • FWIW, this is a pretty reasonable way (the OP's) to override methods eg for testing. We override methods this way to control their implementations, using categories loaded only for a testing target. It's not as dire as all that, IMO. This is a neat trick, though a bit obfuscatory. – tooluser Oct 03 '13 at 15:08