Categories: Modifying Existing Classes in Objective-C
One of the more interesting features of some scripting languages is the programmer's ability to modify existent--and sometimes even built-in--classes. It can be used to segment functionality in a large class, split source for one class between several files, and add convenience methods.
Objective-C also has this capability through the use of Categories.
For an example of where this might get used: I found early on that I missed the convenience of Java's String.trim() method, which would strip the whitespace from both sides of the String and return a new object. Python and Ruby have str.strip() and String.strip, respectively.
In Objective-C we have a similar tool that is very powerful: [NSString stringByTrimmingCharactersInSet:] which takes a NSCharacterSet as an argument. To trim all of the whitespace, I would say:
To trim a specific group of characters, I might call:
This is quite verbose, and (as a trivial example goes) and requires putting together several method calls for what is (for me, anyways) a very common tasks. So for our example, I'll add methods for these functions to NSString.
There are three parts for adding a Category to NSString:
-
Create a header file for the interface.
Create the implementation file.
Import the header file where I want to use the modified class (purely optional).
Header File
Using the convention suggested by Apple and others, I have chosen to name my header file NSString+NGTrim.h, where NSString is the base class that I am modifying and NGTrim is the name I have chosen for my Category.
If I wanted to go for extreme brevity, I could just use the method names trim and trimCharacters:, but here I decided to go with something closer to Apple's naming convention.
The first line is @interface NSString ( NGTrim ) and serves to declare the category. It follows the format @interface BaseClassName ( CategoryName ).
After that come the method names, it is worth noting that we can add methods only and cannot actually change the member variables without subclassing.
Implementation File
- #import "NSString+NGTrim.h"
- }
- return [self stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:trim]];
- }
- @end
This is a standard implementation file.
Usage
- #import "NSString+NGTrim.h" //Optional
- ...
- [@"\t\tTest!\t\t" stringByTrimmingWhitespace];
The methods are now available and can be called from any NSString object in our program.
When we are ready to use our modified class we may optionally call #import "NSString+NGTrim.h". The new methods will function just fine and will be visible at runtime without the import, but at compile time we will see a warning that "'NSString' may not respond to '-stringByTrimmingWhitespace'."
I would generally consider importing such files a Best Practiceâ„¢ for the sake of documentation, warning management, and code cleanliness.
Conclusion
Objective-C gives us a powerful way to segment our classes, add additional functionality, and add convenience methods to existent classes. However, given the power of Categories and their potential for abuse, it is probably a good idea to discuss any modifications to common libraries or core classes (especially NSObject) with your team, and making good use of folder layout so that the modifying files are easily found.
Further Reading
