Categories vs Inheritance in Objective-C

Lets say you need to extend the functionality of the already present NSString class. Dont simply create a new sub-class (unless you really need to define a new sub-type) use catagories instead.

I'd say, when ever you think there's a parent-child relationship that needs to be implemented, use inheritance. If it's simply about extending the functionality of a pre-defined class use catagories. Simple as that.
 
Interesting. I think there is quite a bit of redundancy here, since everything you can do via categories you can do via inheritance, but what you can do via inheritance (define instance variables, for instance) you can't do with categories.

I suppose in the interest of not mucking up an inheritance tree, categories could come in handy. But if you were to add functionality to an existing class like NSString, aren't you already sub classing it? After all, your new MyNSString contains all the methods of NSString, the variables of NSString, and for all intents and purposes is_a NSString, why use categories?

This is just a new concept to me, and as some on the Mac programmers usenet group will say, C++ and Java has caused me brain damage :). Guess overtime, I'll make some sense of when to use categories...
 
Well, the reason of category vs inheiritance is mostly a semantics one. However, there is a good rule of thumb that I use:

If you are adding functions, but not data, use a category. An example of this is adding functions to NSMutableData which allow you to remove X bytes from a section of the data, or remove all BUT X bytes from the data. It doesn't make sense to create an entire sub-class simply to add these two functions, nor does it makes sense to write functions that aren't attached to a class (a plain C function). This allows you to attach the functionality to the class without creating a new, uneeded relationship, and doesn't change any part of existing functionality.

If you need to add/change data and add functionality to manipulate that data, or change functionality to represent a sub-type of object in your controller model, then you sub-class. For example, NSMutableData is a sub-class because it still uses NSData accessors, however, it changes the data representation internally so it can also manipulate the data. In this case a sub-class makes sense because it is no longer NSData, it is a /mutable/ NSData object (new keyword added to describe the object).

Another way to look at this: Do you describe it as a <insert adj> <insert type> object? Or do you describe it as a <insert type> object with X functionality?

Examples:
Mutable NSData object -> NSMutableData subclass of NSData
NSData object with range extraction -> NSData category (range_extraction)
Ford Truck object -> NSFordTruck subclass of NSTruck
Ford Truck object with reclining seats -> NSFordTruck category (reclining_seats)

Also, categories are the only way to implement private functions (declaring them in the .m file instead of the header.

Hopefully this helps? I tried to approach it a few different ways and type until I couldn't think of any other ways to say it. Mostly so that if one way of looking at it is a little odd, another might make more sense.
 
Sounds good so far.

I'm guessing categories is something like interfaces in Java. Will have to get more used to Objective-C to know when they are to be used. But everything has been very helpful. Thanks.
 
Actually, Objective-C has an analog for interfaces: protocols. Protocols is a 'knows' type relationship which allows you to do dynamic typing through partial inheiritance. Another way to help make this clear:

Ford Truck with reclining seats that knows english -> NSFordTruck (NSTruck subclass) (reclining_seats category) (english protocol).

Protocols are exactly like interfaces in Java. Categories add functionality that doesn't change the type of object, and inheiritance changes the type of object. It is something that does take awhile to get a good firm grip on, and I learned how they all mesh and should be used based on trying to write a 3D engine for a college project.
 
Stupid question.

If categories add functionality without changing the type of the object, how do you know that functionality is going to be there? If I added stuff to NSString, how do I know it is going to be there in some other function that accepts a normal NSString?

I may not be making much sense at the end of a long working day, but hopefully I did :).
 
Well the compiler will tell whether it is there or not.

But you just need to include your header file like normal: #include "NSStringCategory.h" or whatever it is named.
 
Yes, i'm not a big fan of categories either, a good design will eliminate their use. But at times its best you dont extend classes to add functionality, specially when it's of a foreign library or collection, i.e it's not created by you. In these cases catagories are very handy. And I suggest you use them likewise.
 
OK, so that means I'll still get a compile time error if I try to access methods in NSString that aren't there if I accidently passed a method an NSString instead of my own NSString with categories?

OT: OO is hard..... straight C was so much easier.
 
No I don't think you're quite getting it...

Categories extend any class, no matter if you wrote the class or Apple did. Categories simply add methods you can use on any class object - the class doesn't even have to be instansiated (could be the + method).

But you can only use these methods that you define if you import the header file into your classes that are wanting to use this method.

For example, let's say you wanted a simple way to write a string to the Desktop. Instead of doing
Code:
[myString writeToFile:[@"~Desktop/file.txt" stringByExpandingTildeInPath] atomically:YES];
you could put it into a category and call it wherever you include that category's header file:

NSStringXtras.h file
Code:
#include <Cocoa/Cocoa.h>

@interface NSString (MyExtension)
- (void)writeToDesktopWithName:(NSString *)fileName;
@end
NSStringXtras.m file
Code:
#include "NSStringXtras.h"

@implementation NSString (MyExtension)
- (void)writeToDesktopWithName:(NSString *)fileName
{
  // "self" here is the NSString object (myString)
  [self writeToFile:[[@"~Desktop" stringByAppendingPathComponent:fileName] stringByExpandingTildeInPath] atomically:YES];
}
@end
So now in any other class, you can #import "NSStringXtras.h" and any time you have an NSString object, it has an extra method available named "writeToDesktopWithName:"
Code:
[myString writeToDesktopWithName:@"file.txt"];
Hope this helps.
 
Yes, definitely keep in mind that categories aren't used often. Normally, you don't have a 'with' relationship when doing the initial design work, but when using someone else's API, you tend to ask yourself: "What if this class could do X? It would make life simpler", also categories declared in the class' source file help make certain parts of the class private.

So, there should only be two points where you use categories in your own class hierarchy: private functions, and organizing large classes into smaller source files (Apple uses it this way, by taking larger classes and splitting them up into categories based on function).

If you have full control of your class heirarchy, then you can actually add the functions into the class itself, and should unless there is a specific reason.
 
Back
Top