Cocoa Programming for Mac OS X - Aaron Hillegass [95]
Challenge 2
In the RaiseMan project, add a menu item that triggers the removeEmployee: method in RMDocument.
Chapter 22. Categories
Although the engineers at Apple are very wise, one day you will think, “Golly, if only they had put that method on that class, my life would be so much easier.” When this happens, you will want to create a category. A category is simply a collection of methods that you would like added to an existing class. The category concept is very useful, and it is surprising that so few object-oriented languages include this powerful idea.
Creating categories is easier than talking about them. In the previous chapter, you added pasting capabilities to your BigLetterView. Note, however, that if the string on the pasteboard has more than one letter, the paste attempt will fail because BigLetterView is capable of displaying only one letter at a time. Let’s extend the example to take just the first letter of the string instead of failing.
Add a Method to NSString
It would be nice if every NSString object had a method that returned its first letter. It does not, so you will use a category to add it.
Open the TypingTutor project and create a new file of type Objective-C category. In the Category field enter FirstLetter, and for Category on enter NSString. Two files will be created: NSString+FirstLetter.m and NSString+FirstLetter.h. Edit NSString+FirstLetter.h to look like this:
#import @interface NSString (FirstLetter) - (NSString *)bnr_firstLetter; @end You appear to be declaring the class NSString, but you are not giving it any instance variables or a superclass. Instead, you are naming the category FirstLetter and declaring a method. A category cannot add instance variables to the class, only methods. Now implement the method bnr_firstLetter in the file NSString+ FirstLetter.m. Make the file look like this: #import "NSString+FirstLetter.h" @implementation NSString (FirstLetter) - (NSString *)bnr_firstLetter { if ([self length] < 2) { return self; } NSRange r; r.location = 0; r.length = 1; return [self substringWithRange:r]; } @end Now you can use this method as if it were part of NSString. In Big LetterView.m, change readFromPasteboard: to look like this: - (BOOL)readFromPasteboard:(NSPasteboard *)pb { NSArray *classes = [NSArray arrayWithObject: NSString class]]; NSArray *objects = [pb readObjectsForClasses:classes options:nil]; if ([objects count] > 0) { // Read the string from the pasteboard NSString *value = [objects objectAtIndex:0]; [self setString:[value bnr_firstLetter]]; return YES; } return NO; } At the beginning of BigLetterView.m, import the header: #import "NSString+FirstLetter.h" Build and run your application. You will be able to copy strings with more than one letter into BigLetterView. Only the first letter of the string will be copied. In this example, you added only one method, but note that you can add as many methods to the class as you wish. Also, you used only the methods of the class here, but you can also access the class’s instance variables directly. Notice that I added a prefix “bnr_” to the method name in my category. I would like to just name the method firstLetter. But, what if Apple adds a firstLetter method to NSString in Mac OS X 10.9? There would be a conflict. For safety, I added the prefix. Also, note the file naming convention: ClassName+CategoryName.h. Stylish Objective-C developers name their category files in this fashion to clearly indicate the class and category names. The purpose of NSString+FirstLetter.h is much more apparent than FirstLetter.h. Cocoa itself has many categories. For example, NSAttributedString