Our thoughts


Industry insights and market opinion from our CEO, Simon Lee. Articles cover app development and everything Apple from our leading app developer.

Objective-C Categories Explained

Code

Good app developers know how to save time, this generally means no copy-and-paste, no duplicate code and a good class structure to keep common items grouped together in an efficient class hierarchy. One way to add functionality to an existing class is to subclass it. We create a new descendent of our class, add the required functionality and use the new class in place of the old one.

This approach of subclassing is common to all object-orientated language and is one of the key features that enable developers to extend and enhance existing code. Objective-c however, has another way to extend existing classes, a powerful system called Categories. Whilst we can add more classes to our hierarchy to enhance functionality, more classes lead to more complexity, which will make our code more difficult to manage.

By using categories, we can alter the very definition of the existing objects we work with every day. What's more, any object that is derived from a categorised object will include the new logic we have implemented. Powerful indeed.

The source code for this article can be downloaded here.

My First Category!

Let's start with the format of a category. At first glance a category definition looks very similar to a normal class definition. We first define an interface for the object we want to categorise.

[objc]
@interface MyClass (CategoryName)

//Add method signatures here

@end
[/objc]

Notice the parentheses after the class name, these group the following methods under a particular name. It is good practice to state in the parenthesis the purpose of the category. After a while we may build up a large collection of tailored categories, so good naming is essential. A fairly standard naming convention for the header file containing the interface would be: "ClassName+CategoryName.h", for instance if we were extending the NSString class with formatting utility methods we may have the following...

[objc title="NSString+FormattingUtils.h"]
#import <Foundation/Foundation.h>

@interface NSString (FormattingUtils)

// Add method signatures here

@end
[/objc]

Similarly to a normal class, the implementation of this class would look like this...

[objc title="NSString+FormattingUtils.m"]
@implementation NSString (FormattingUtils)

// Add method implementations here

@end
[/objc]

Let's try a more concrete example. I have an NSString, and I want to know if it contains the string "Locassa". I could write a code to check the value of a string against this search term, but what if I make the call a lot? I could put the method in a base class, but then I am forcing my class hierarchy based on a single method. Instead we can add a category to the NSString class as follows...

[objc title="NSString+Contains.h"]
#import <Foundation/Foundation.h>

@interface NSString (Contains)

- (BOOL) containsLocassa;

@end
[/objc]

Implementing the interface we get:

[objc title="NSString+Contains.m"]
#import "NSString+Contains.h"

@implementation NSString (Contains)

- (BOOL) containsLocassa {
return !([self rangeOfString:@"Locassa"].location == NSNotFound);
}

@end
[/objc]

Easy enough. To make use of this we simply import the header file into wherever we need to use it, and call as if it were a method on any other NSString object.

[objc]
#import "NSString+Contains.h"

......

NSString *someString = [[NSString alloc] initWithString:@"Locassa is awesome"];

if([someString containsLocassa]) {
NSLog(@"The string contains 'Locassa'");
}

[someString release];
[/objc]

As you can see we don't need to do anything with our existing NSString objects, simply include the header file and the method can be called on existing variables. This makes adding functionality to an existing system really easy.

Categories are not limited to extending classes. We can also override existing methods within a class. If I need some custom logic that a UIView doesn't provide, I can override the method required while still having access to the original declaration. Pretty powerful huh!

With great power....

It all seems so simple, and it is! There are however some things worth considering.

  • Naming conflicts: If we have several categories imported, both overriding or defining the same method, it can lead to confusing results, this is where a solid naming convention is important.
  • Overriding existing behaviour: Overriding a method in a category does give certain advantages, but remember, it affects all objects of the categorised type. If you are overriding a method from a derived class, you still have access to the "super" implementation, however, overriding a method declared only in the class we are categorising causes us to lose the original implementation
  • New methods are added to the categorised object: When using a framework, we may see that it does not do all the things we want it to do, so we may decide to use categories to extend it. Should we update to a later version of the framework, and the object is given a new method with the same signature, it is our method that is used.
  • Categories are not a catch all solution: Though extremely useful, a category is not always required. Knowing when to use them and when not to is very important. Adding extension methods that contain business logic rather than subclassing is not a great idea.

What Categories CAN and CANNOT do

A category can provide a great way to introduce convenience methods. They can also be used to divide a particularly complex class into separate implementations, each dealing with a different aspect of the class.

Categories cannot introduce new instance variables, this requires a subclass. Also, multiple categories cannot have the same name.

Final Words

Remember, categories are not a replacement for subclassing, they are merely there to allow you to extend existing objects when you only need simple functionality added. They can help when the overhead of creating a subclass and replacing all instances of the original class is not worth the added value that subclassing can provide.

We have added our most commonly used categories to this post. Enjoy.