Objective-C supports inheritance (but not multiple inheritance) like any good object oriented language. However, like many newer OOP languages, such as C# and Ruby, Objective-C 2.0 also supports extension methods, which in Objective-C parlance are called Categories. If you are at all familiar with C# (3.0 and on) you might have created extension methods, extending the functionality of a pre-existing class, such as the String
class, by defining new functions to handle compression or specific cases of localization.
Categories in Objective-C allow you to add new functions to an already existing class without deriving a whole new class from some arbitrary base class. You effectively extend an already existing class to acquire some new functionality without all the extra overhead.
Declaring a new category is almost identical to declaring the interface and implementation for a class. Declare the interface and implementation as normal, and in brackets, immediately after the class name in either the @interface
or @implementation
line of the class being extended, add the name of the category.
For example:
@interface NSString (ReallyCoolExtension)
… extension methods …
@end
Which would add your ReallyCoolExtension to the NSString class.
By adding category extension methods to a class with the same name and signature of currently existing class methods you are also able to override the default behaviour of classes. Want to change upperCaseString
to actually returned a CamelCaseString instead? You can do that. Of course, overriding established methods in classes is not always wise, especially if the base classes are part of the standard foundation library.
Under normal circumstances categories only permit methods to be added to a class, not instance variables. It is possible to add new variables, after a fashion, to established classes, but that topic is beyond the scope of this short article.
When declaring a new category, the name of the category must be unique within the application. Two categories with conflicting names will cause unexpected application behaviour.
The example code below shows both the interface, the implementation, and the main()
function to test the code all in a single file. Placing the @interface
code in to a header .h
file and the @implementation
code in to the implementation .m
file is of course the standard practice. The file naming convention for a category is to use the name of the class that the category extends, followed by a plus sign, +, followed by the name of the category. So in the example code below, the file names would be:
NSString+InverseCapitalisation.h
NSString+InverseCapitalisation.m
The following example program adds a new category to the NSString
class that converts the characters in the string to upper case. Once the category has been added to the NSString
class, all NSStrings
will be able to utilise the new functionality.
WARNING: The example code provided works on individual characters within the NSString object using plain C functions such as toupper()
and isupper()
. This code is not compatible with anything other than the most basic ASCII
character set. The reason to use the standard C library for this example is to illustrate how Objective-C 2.0 Categories work, not to delve in to the esoteric subtleties of Unicode.
NSString
class.
Category
extension method to perform an inverse capitalisation on a string.NSString
object, changing lower-case letters to upper-case, and vice versa.NSString
containing mixed case text.#import <Foundation/Foundation.h>
@interface NSString (inverseCapitalisation)
- (NSString *) inverseCapitalisationString;
@end
@implementation NSString (inverseCapitalisation)
- (NSString *) inverseCapitalisationString
{
// This is a quick hack to convert upper case letters to lowercase, and vice versa
// It uses the standard C character manipulation functions so it will obviously not
// work on anything other than basic ASCII strings.
// get the length of the string that is to be manipulated
int len = [self length];
// create a string to hold our modified text
NSMutableString *capitalisedString = [NSMutableString stringWithCapacity:len];
// iterate over the original string, pulling out each character for inspection
for (int i = 0; i < len; i++)
{
// get the next character in the original string
char ch = [self characterAtIndex:i];
// convert upper-case to lower-case, and lower-case to upper-case
if (isupper(ch))
{
ch = tolower(ch);
}
else if (islower(ch))
{
ch = toupper(ch);
}
// append the manipulated character to the modified string
[capitalisedString appendString:[NSString stringWithFormat:@"%c", ch]];
}
// return the newly modified string
return capitalisedString;
}
@end
int main (int argc, const char * argv[])
{
// standard Foundation housekeeping
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// define a sample string to be inverse capitalised
NSString *string = [NSString stringWithString:@"Hello World!"];
// capitalise the string
NSString *capital = [string inverseCapitalisationString];
// output the original, unmodified string
NSLog(@"String : %@", string);
// output the capitalised version of the string
NSLog(@"Inverse Capitalised: %@", capital);
// standard Foundation housekeeping
[pool drain];
return 0;
}
2010-10-11 03:17:34.766 InverseSample[688:a0f] String : Hello World!
2010-10-11 03:17:34.780 InverseSample[688:a0f] Inverse Capitalised: hELLO wORLD!
The results show that the original input string of “Hello World!” has been converted to “hELLO wORLD!” by inverting the case on each of the letters in the NSString
. The inverseCapitlisationString
function could have performed any number of operations on the NSString
, and all future NSString
objects declared within the application will have the functionality to perform this transformation very simply as though invoking any normal method on the NSString
object.
Categories, A.K.A. extension methods are a great way to add functionality to an already existing class without having to completely derive a new class, which in many cases of where categories are useful, is not a desirable thing to do.