Objective-C Categories Explained

Objective-C Categories Explained
NERD ALERT! This is a tech article, the faint of heart should browse on…
WE ARE HIRING! Want to work for us? Head to our careers page for more information… Locassa Careers

Overview

Good 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.

@interface MyClass (CategoryName)
//Add method signatures here
@end

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…

#import <Foundation/Foundation.h>
@interface NSString (FormattingUtils)
// Add method signatures here
@end

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

@implementation NSString (FormattingUtils)
// Add method implementations here
@end

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…

#import <Foundation/Foundation.h>
@interface NSString (Contains)
- (BOOL) containsLocassa;
@end

Implementing the interface we get:

#import "NSString+Contains.h"
@implementation NSString (Contains)
- (BOOL) containsLocassa {
	return !([self rangeOfString:@"Locassa"].location == NSNotFound);
}
@end

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.

#import "NSString+Contains.h"
......
NSString *someString = [[NSString alloc] initWithString:@"Locassa is awesome"];
if([someString containsLocassa]) {
	NSLog(@"The string contains 'Locassa'");
}
[someString release];

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.

James

James is as talented as he is dedicated. A true iOS end-user he has worked on many of the major Locassa projects since it’s launch in 2009. James has an eye for detail which most other developers miss and is a key member of the Locassa management team.

More Posts

Add comment

Comments

  1. Daniel Hanly Reply June 13, 2011 at 12:55 pm

    Very informative. This will help me on my project when I have to make some complex functions!

    • Simon Reply June 17, 2011 at 2:25 pm

      Hi Daniel, glad you found it useful. Keep an eye out on our blog (especially the tech articles), we have a lot more posts coming on a variety of topics!

  2. Brett Musick Reply July 24, 2011 at 11:17 pm

    Thank you very a lot!! It is extremely good ~

  3. lakshmipathi Reply March 8, 2013 at 5:54 pm

    Thanks……………it is very nice..!!!!!!!!!!!

  4. priya Reply April 9, 2013 at 11:03 am

    Can anyone tell me how to use ivars in categories through Associated References ?ANfd how to alloc them?(like we alloc ivars in init method )

  5. priya Reply April 9, 2013 at 11:05 am

    Thanks In Advance.