20

The following is working in objective c:

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

//Category in ClassA+CategoryB.h and ClassA+CategoryB.m
@interface ClassA (CategoryB) 
- (NSString *) myMethod;
@end
@implementation ClassA (CategoryB)
- (NSString*) myMethod { return @"B"; }
@end

The question is, if I am just importing ClassA.h and send the message

[myClassA myMethod]; //returns B

why is this returning B? I am not importing ClassA+CategoryB

Even futhrer, if I did the following:

// Base Class in ClassA.h and ClassA.m
@interface ClassA : NSObject 
- (NSString *) myMethod;
- (NSString *) mySecondMethod;
@end
@implementation ClassA
- (NSString*) myMethod { return @"A"; }
- (NSString *) mySecondMethod { return [self myMethod]; }
@end

//Category in ClassA+CategoryB.h and ClassA+CategoryB.m
@interface ClassA (CategoryB) 
- (NSString *) myMethod;
@end
@implementation ClassA (CategoryB)
- (NSString*) myMethod { return @"B"; }
@end

and call mySecondMethod:

ClassA *a = [[ClassA alloc] init];
NSLog(@"%@",[a myMethod]);

the result will still be B although nobody knows (due to no import) of the category implementation?!

I'd excepted, only to return Bif I was importing the category...

So any hints appreciated.

Gabriele Petronella
  • 106,943
  • 21
  • 217
  • 235
Alexander
  • 7,178
  • 8
  • 45
  • 75
  • 6
    I don't know the answer to your question, but categories are usually for adding new methods to a class, not to override the existing ones. If you want to do that, you should subclass it. – Levi Jan 10 '13 at 13:58
  • 1
    Overriding methods in categories results in undefined behavior: Case in point. – CodaFi Jan 10 '13 at 14:00
  • 1
    Thanks Levi for your answer, I know that but I was questioning the above example for learning purposes... I didn't expect, that's why I am asking. – Alexander Jan 10 '13 at 14:01
  • btw it's a dupe of http://stackoverflow.com/questions/7604813/objectivec-category-is-not-imported-but-still-running-code – Gabriele Petronella Jan 10 '13 at 14:09

3 Answers3

40

Objective-C messaging is dynamic, this means that it doesn't matter if you import or not the category. The object will receive the message and execute that method.

The category is overriding your method. This means that when the runtime sends a message to that object, will always find the overridden method, no matter what you import.

If you want to ignore a category you shouldn't compile it, so you could remove the category from the compiler sources.
An alternative is subclassing.

Also read this:

Avoid Category Method Name Clashes

Because the methods declared in a category are added to an existing class, you need to be very careful about method names.

If the name of a method declared in a category is the same as a method in the original class, or a method in another category on the same class (or even a superclass), the behavior is undefined as to which method implementation is used at runtime. This is less likely to be an issue if you’re using categories with your own classes, but can cause problems when using categories to add methods to standard Cocoa or Cocoa Touch classes.

So in your case you haven't got any problem because as stated, this is less likely to happen with user defined classes. But you should definitely use subclassing instead of writing a category.

Community
  • 1
  • 1
Ramy Al Zuhouri
  • 21,580
  • 26
  • 105
  • 187
  • That's true & I know that, but I do not really understand the hierarchy - the category method being called instead of the class method. I'd expected it to be the other way around... – Alexander Jan 10 '13 at 14:00
  • It doesn't matter what you link, the category is compiled and that method is overridden. The runtime sends a message to the object and always finds the overridden method. – Ramy Al Zuhouri Jan 10 '13 at 14:06
  • Instead of overriding the method, swizzle the class itself. It is safer. – Reaper Sep 14 '17 at 15:02
10

Obj-C allows you to add methods to an existing class using Categories. So if you add a method to NSString, the categoriesed method is available to NSMutableString and all classes which inherits NSString or any subclasses of NSString.

But you should avoid to Override the category.

You will not be 100% sure which method will be called. It depends on compiler.

From Apple Documentation.

Although the Objective-C language currently allows you to use a category to override methods the class inherits, or even methods declared in the class interface, you are strongly discouraged from doing so. A category is not a substitute for a subclass. There are several significant shortcomings to using a category to override methods: 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 exists in the category's class, there is no way to invoke the original implementation. A category cannot reliably override methods declared in another category of the same class. This issue is of particular significance because many of the Cocoa classes are implemented using categories. A framework-defined method you try to override may itself have been implemented in a category, and so which implementation takes precedence is not defined. The very presence of some category methods may cause behavior changes across all frameworks. For example, if you override the windowWillClose: delegate method in a category on NSObject, all window delegates in your program then respond using the category method; the behavior of all your instances of NSWindow may change. Categories you add on a framework class may cause mysterious changes in behavior and lead to crashes.

Anoop Vaidya
  • 46,283
  • 15
  • 111
  • 140
  • Yes you'r absolutely right and I know that. But isn't my example a little bit confusing? Nobody knows about the category method but it's being called?! Like I said, I didn't expect it to be called and woundering why the category method is "priorized higher than the class method" – Alexander Jan 10 '13 at 14:04
  • So basically you are saying, other compiler possible other output? – Alexander Jan 10 '13 at 14:05
  • You got your answer from http://stackoverflow.com/questions/5272451/overriding-methods-using-categories-in-objective-c. There's nothing wrong about that, but at least check link sanity! – Gabriele Petronella Jan 10 '13 at 14:06
  • I check once even in my system, once I changed the path, it called other method. Even u can not add a method say `addObject:` for NSMutableArray. But for your custom class you can. So why to live dangerously. Play safe... subclass or use anotherMethod. – Anoop Vaidya Jan 10 '13 at 14:07
  • @GabrielePetronella :Thanks, I dint check for broken link. – Anoop Vaidya Jan 10 '13 at 14:09
  • actually... sending the message to super from a category method that overrides the main class's method will trigger the main class' parent class' method. If B is a subclass of A and BC is a category on B, then using super within the override method in BC will message A, not B. A category can redefine the method but cannot easily augment a method by way of using super in the category's method. – Cerniuk May 29 '17 at 16:32
2

You implicitly included the code defined in the category by compiling it.

If you want to avoid the category code to be executed you should remove it from your target, by removing the category implementation file. You can do that from

Target->Build Phase->Compile Sources

That said, you should never use a category to override a method. That's a very bad practice and it's not what categories are for.

Gabriele Petronella
  • 106,943
  • 21
  • 217
  • 235