Categories In Objective-C

Thu, Aug 7

As an alternative to subclassing, Objective-C categories provide a means to add methods to a class. What’s intriguing, is that any methods that you add through a category become part of the class definition, so to speak. In other words, if you add a method to the NSString class, any instance, or subclass, of NSString will have access to that method.

Defining a category is identical to defining the interface for a class, with one small exception: you add a category name inside a set of parenthesis after the interface declaration. The format is shown below:

1
2
3
@interface ClassToAddMethodsTo (category)
  ...methods go here
@end


For example, below I’ve defined a category that adds a method to the NSString class. The method reverseString adds the capability to all NSString objects to reverse the characters in the string.

1
2
3
@interface NSString (reverse)
-(NSString *) reverseString;
@end

As with the @interface declaration, the @implementation section changes only in that the category name is added to the definition. Below is the implementation of the interface defined above. Notice how in both cases I added (reverse), which is the category name I assigned.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@implementation NSString (reverse)
 
-(NSString *) reverseString
{
  NSMutableString *reversedStr;
  int len = [self length];
 
  // Auto released string
  reversedStr = [NSMutableString stringWithCapacity:len];     
 
  // Probably woefully inefficient...
  while (len > 0)
    [reversedStr appendString:
         [NSString stringWithFormat:@"%C", [self characterAtIndex:--len]]];   
 
  return reversedStr;
}
 
@end

What follows is a short example to showing how one might use the above category.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#import <Foundation/Foundation.h>
#import "NSString+Reverse.h"
 
int main (int argc, const char * argv[])
{
  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  NSString *str  = [NSString stringWithString:@"Fubar"];
  NSString *rev;
 
  NSLog(@"String: %@", str);
  rev = [str reverseString];
  NSLog(@"Reversed: %@",rev); 
 
  [pool drain];
  return 0;
}

The output of the above example is shown here:

Overriding Methods

Categories can also be used to override methods the class inherits, again, providing an alternative to subclassing. You can always access the overridden method using super. I would assume from an internal (compiler/code generation) perspective, this could lead to less code/overhead as compared to creating a subclass solely to override a method (caveat: I have no proof that is true).

Dividing Source Code into Separate File

Although I haven’t given this a go, categories provide an interesting opportunity to disperse the implementation of a class across one or more files. The Objective-C Programming Guide lists several benefits of this including:

  • Opportunity to group together methods that perform similar tasks.
  • Configuring classes differently for various applications, yet maintaining one set of code.

Naming Conventions

The recommended naming convention for a category is “ClassToAddMethodsTo+CatgoryName” for instance, I used the following filenames for the interface and implementation of the above category code:

  • NSString+Reverse.h
  • NSString+Reverse.m

Caveats

You cannot add instance variables to a class through a category. Also, category names must be unique across an application.

Source Code

I’ve attached the Xcode project for the above example if you’d like to give it a try.

In the next post I’ll show another example of using categories – the example will show one approach for hiding methods within in a class, as Objective-C does not offer support for private methods.

20 comments

Thanks for this post. Nice, clear explanation. I appreciate it.

by Jake on Jun 29, 2009. #

I already knew how categories work, but your post is really well written and clear.
So I felt to write down a congrats comment.

by Cesare on Jul 21, 2009. #

Thanks, very clear and detailed explanation. Can I add new member variables (it seems i cannot, but you might have some tip).

by nir on Aug 13, 2009. #

nir, unfortunately, you cannot add instance variables to a class.

by John Muchow on Aug 13, 2009. #

Great post John,

I tried to use the same method to have a category overwrite viewDidLoad of a UIViewController class. But the problem is that the category implementation is unable to call the [super viewDidLoad method]. The compiler gives me a warning that the viewDidLoad method is not part of UIResponder. Strange.

Here is the category code

// Header
@interface UIViewController (UIViewController_TestingCategory)
– (void) viewDidLoad;

@end

// Implementation file

#import “UIViewController+TestingCategory.h”

@implementation UIViewController (UIViewController_TestingCategory)

- (void)viewDidLoad {
[super viewDidLoad];

// Do my own special behavior of view did load
….
}

-> The compiler gives me the warning on [super viewDidLoad] and of course things crash.

Do I have to revert to subclassic. I would like to use categories so I don’t have to change my view controllers to inherit from my own UIViewController class.

Thanks,
Leifur

by Leifur Bjorn on Dec 7, 2009. #

Can cateogeory be applied to ui objects i mean (view, tabbars and navigation bars etc..)

by pramod on Jan 12, 2010. #

pramod, I would imagine you could use categories with ui objects as they as simply another object.

by John Muchow on Jan 12, 2010. #

@Leifur Bjorn when you shadow a method with a category, you lose access to the original method. You can think of categories like adding items to a class or instance method dictionary. If you add a value for a key that already exists, you’ll overwrite the existing value. (There are ways to get around this at runtime by saving the pointer to the old method, but not at compile time.)

To access the original method you must subclass.

by Jonathan Hohle on Jan 12, 2010. #

F.Y.I. Posible Error with naming classes and use of categories.

I have used categories to breakup a class into multiple files with no problems in the past. I tried to do the same with a class name (“Game_2ViewController+Animation.h”) that had an underscore character and the methods and functions defined within the subcategory class file could not be called by the program. I simply made a new subcategory class file and used no underscore character (“Game2ViewController+Animation.h”) and the problem went away?

by Mark Sanchez on Jan 21, 2010. #

Hey John,

I just a very efficient way to reverse a string and started with your lines of codes. Here is what I came up at the end. Hope this will help for some people. :-)

@implementation NSString (reverse)
- (NSString *) reverseString {
NSMutableString *reversedStr;
int len = [self length];

// Auto released string
reversedStr = [NSMutableString stringWithCapacity:len];

while (len–) {
[reversedStr appendFormat:@"%C", [self characterAtIndex:len]];
}

return reversedStr;
}

by Sascha Hameister on Mar 28, 2010. #

Thank you!
With this perfect explanation i just managed my write my first category that saves me typing when loading images in a imageview when “imagenamed” would clutter the ram / cache.
Thank you! Thank you! Thank you!

by Sebastian on May 21, 2010. #

@Leifur

The Apple documentation actually states that it’s a bad idea to use categories to override instance or class methods: http://developer.apple.com/mac/library/documentation/cocoa/conceptual/objectivec/articles/occategories.html

The relevant bit for you is the first bullet point in this section:

“Although the language currently allows you to use a category to override methods the class inherits, or even methods declared in the class interface, you are strongly discouraged from using this functionality. A category is not a substitute for a subclass. There are several significant shortcomings:

* When a category overrides an inherited method, the method in the category can, as usual, invoke the inherited implementation via a message to super. However, if a category overrides a method that already existed in the category’s class, there is no way to invoke the original implementation.

* A category cannot reliably override methods declared in another category of the same class.

This issue is of particular significance since many of the Cocoa classes are implemented using categories. A framework-defined method you try to override may itself have been implemented in a category, and so which implementation takes precedence is not defined.

* The very presence of some methods may cause behavior changes across all frameworks. For example, if you add an implementation of windowWillClose: to NSObject, this will cause all window delegates to respond to that method and may modify the behavior of all instances of NSWindow instances. This may cause mysterious changes in behavior and can lead to crashes.”

…hope that helps!
Nick

by Nick D on May 31, 2010. #

Thanks for this explanation…

by stef13 on Jan 8, 2011. #

About the file name in the project. They are

NSString+reverse.h (NOT NSString+Reverse.h)
NSString+Reverse.m

In the NSString+Reverse.m we see
#import “NSString+Reverse.h” (NOT NSString+reverse.h)

However, it works. Why?

Alex

by Alex on Jun 30, 2011. #

Seems Xcode treats filenames as case-insensitive. Either way, it was a typo on my end – I’ve fixed and uploaded a new version of the project.

John

by John Muchow on Jul 1, 2011. #

Hi, thanks for this comment :)

by Betsey Ostorga on Aug 15, 2011. #

I’m new to the Objective-C programming, but as I can see both from my experiments in XCode 4.2.1 and from “Programming in Objective-C 2.0″ by S. Kochan, when overriding some method in a category, developer is NOT able to execute a method from the parent class by using a ‘super’ keyword.

Please correct me if I’m wrong.

by DD on Nov 28, 2011. #

Hi, This is very nice post, help me to make my concepts about category very clear, and also this helps me to crack my some interview questions.

Thanks a lot.

by Bhuru on Jun 8, 2012. #

Actually, you can add instance variables if you leave out the name in parens:

@interface MyClass ()

@property (weak, nonatomic) NSString *lookMaAProperty;

@end

by Maurice on Apr 11, 2013. #

Nice tip Maurice, thank you.

by John Muchow on Apr 11, 2013. #