Categories let a programmer add methods to any existing class. For example, Apple gave us the class NSString. Apple does not share the source code to that class, but you can use a category to add new methods to it.
Create a new Foundation Command Line Tool called VowelCounter. Then create a new file that is an Objective-C category. Name the category BNRVowelCounting and make it a category on NSString.
Now open NSString+BNRVowelCounting.h and declare a method that you want to add to the NSString class:
#import <Foundation/Foundation.h> @interface NSString (BNRVowelCounting) - (int)bnr_vowelCount; @end
Now implement the method in NSString+BNRVowelCount.m:
#import "NSString+BNRVowelCounting.h" @implementation NSString (BNRVowelCounting) - (int)bnr_vowelCount { NSCharacterSet *charSet = [NSCharacterSet characterSetWithCharactersInString:@"aeiouyAEIOUY"]; NSUInteger count = [self length]; int sum = 0; for (int i = 0; i < count; i++) { unichar c = [self characterAtIndex:i]; if ([charSet characterIsMember:c]) { sum++; } } return sum; } @end
Now use the new method in main.m:
#import <Foundation/Foundation.h> #import "NSString+BNRVowelCounting.h" int main (int argc, const char * argv[]) { @autoreleasepool { NSString *string = @"Hello, World!"; NSLog(@"%@ has %d vowels", string, [string bnr_vowelCount]); } return 0; }
Build and run the program. Nifty, eh? Categories turn out to be very useful.
It is important to note that only this program has the category. If you want the method available in another program, you must add the files to your project and compile the category in when you build that program.
Notice also that the method that you wrote begins with bnr_. When you implement a method using a category, it replaces any method with the same name that already exists on the class. So if, in the future, Apple implements a method called vowelCount on NSString, you don’t want your method to stomp on theirs. Thus, it is a good idea to add a prefix like this to the names of any methods you add to Apple’s classes using a category.
You should use categories to add functionality to existing classes. Do not use them to replace functionality in existing classes; use subclassing instead.
Create a new Foundation Command Line Tool called DateMonger. Add an NSDate category named BNRDateConvenience.
In the category, add a class method to NSDate that takes three integers (year, month, and day) and returns a new NSDate instance that is initialized to midnight on the passed-in day.
Test it in main().
Hint: You will want to refer to the NSDateComponents class that you learned about in Chapter 14.