Chapter 10: Extending Classes with Categories

Matthew Campbell, December 4, 2011

Much of the day to day work in object-oriented programming involves using classes that are already written. Sometimes we want our classes to do just a little bit more. That means they need more properties and methods and adding these entities changes the nature of the class. This is called sub-classing and we already covered how to do that.

There are times, however, when we want to extend the abilities of class but we don’t want to define a completely new class. You will encounter these situations more and more as you plunge into Objective-C programming. Perhaps more importantly, you will find examples of category use throughout the Mac and iOS programming examples written by Apple and other developers.

So, to extend a class without sub-classing you have the option of using categories. Let’s talk about how to do this.

Defining a Category

In the last chapter we worked with a class called myClass so let’s continue to use this class as an example. Let’s assume that we are working with myClass but we need one more method that myClass does not currently support. Only our application delegate needs this method and so it we do not want every component in our system to have access to this very specialized method.

This is a great opportunity to use a category. A category definition looks like a class definition in that it has an interface and an implementation definition. To keep things simple I just put it into the file I am working with at the time before the implementation. However, you could also put them into an external file and use an import directive if you want to share categories.

For now I am just going to add this to the application delegate and I will put code right after the import directives and before the application delegate’s own implementation:

#import "ObjCExamplesAppDelegate.h"
#import "myClass.h"

@interface myClass (AddToMyClass)
- (int) numOfStuff;
@end

@implementation myClass (AddToMyClass)
- (int) numOfStuff{
     return 5;
}
@end

@implementation ObjCExamplesAppDelegate
@synthesize window;

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [window makeKeyAndVisible];
}

@end

The two highlighted regions above are the interface and implementation for the AddToMyClass category. As you can see these look like class definitions except that these do not use the colon indicating that they are a subclass but they have the category name in parenthesis instead. Defining the methods works the same way as it does for classes.

Now that we have included this category definition we can use the numOfStuff function from within the application delegate. You use these methods like any other:

#import "ObjCExamplesAppDelegate.h"
#import "myClass.h"

@interface myClass (AddToMyClass)
- (int) numOfStuff;
@end

@implementation myClass (AddToMyClass)
- (int) numOfStuff{
     return 5;
}
@end

@implementation ObjCExamplesAppDelegate
@synthesize window;

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
	
     myClass *object = [[myClass alloc] initWithName:@"MyObject"];
     NSLog(@"Numbers of Stuffs: %i", [object numOfStuff]);
	
    [window makeKeyAndVisible];
}

@end

Using Categories with Standard Objects

One clever thing that you may find yourself doing with categories is using them with the regular foundation classes. It is a good way to quickly extend these classes when especially when it would be confusing to use your own subclasses of common classes like NSString.

Extend NSString

Here is an example of how and why you may want to do this. I commonly need to enclose strings into HTML tags like <p> and <h3> in my blogging application. I could simply subclass NSString and maybe call it NSHTMLString or something and give that class new methods to make adding tags a little easier.

I could simply add a category as an alternative. So, here is how I would do that in my application delegate:

#import "ObjCExamplesAppDelegate.h"
#import "myClass.h"

@interface NSString (HTMLTags)
- (NSString *) encloseWithParagraphTags;
@end

@implementation NSString (HTMLTags)
- (NSString *) encloseWithParagraphTags{
      return [NSString stringWithFormat:@"<p>%@</p>",self];
}
@end

@implementation ObjCExamplesAppDelegate
@synthesize window;

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
      NSString *text =@"Have you ever read a blog post?";
      NSLog(@"%@", [text encloseWithParagraphTags]);
      [window makeKeyAndVisible];
}

@end

All I need to do to turn my string into a correctly tagged paragraph is send the encloseWithParagraphTags message. Here is the output:

ObjCExamples[644:20b] <p>Have you ever read a blog post?</p>

Summary

In this chpater you learned an alternative to subclassing when you want to extend class functionality. You may not need to use this technique in the beginning, but using categories is a simple trick that could make your life easier down the road.

Hands On Time

Experiment with categories by adding methods to the NSString class that you think could be useful. Try to use this method by putting the category definition in a separate file and using an import directive to share the category definition among at least two other classes in a project.