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.
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 “Glance”. 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) containsGlance;
@end
[/objc]
Implementing the interface we get:
[objc title=”NSString+Contains.m”] #import “NSString+Contains.h”@implementation NSString (Contains)
– (BOOL) containsGlance {
return !([self rangeOfString:@”Glance”].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:@”Glance is awesome”];
if([someString containsGlance]) {
NSLog(@”The string contains ‘Glance'”);
}
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!
It all seems so simple, and it is! There are however some things worth considering.
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.
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. If you have an app idea, Talk To Us today!