When writing our own program, you’ll often want to add some new method to an existing class. You can always add a subclass to an existing class if you want to add some more method to it. But there are limitations to that, not every time subclassing is convenient enough for people to subclass. For example, you want to add an email address validator method to NSString, but NSString is the top of the hierarchy of the class cluster, which makes it difficult to subclass.
Note: A class cluster is an architecture that groups a number of private, concrete subclasses under a public, abstract superclass. The grouping of classes in this way provides a simplified interface to the user, who sees only the publicly visible architecture. Read more about class clusters here: http://developer.apple.com/library/mac/#documentation/General/
Conceptual/DevPedia-CocoaCore/ClassCluster.html
But because of the difficulties, Objective-C has this brilliant mechanism that will let you add a new method to an existing class without any difficulties. The term for is called Categories.
Creating a Category Method
A category is a way to add a new method to an existing class even if you don’t have the source code for it. Let’s say you want to add an NSString method that will validate a string if it’s an email address or not. Of course, without creating a new category, what you’ll do is to write it on your code, what if you needed to validate email address on more than one page of you app? You’ll write it again and again? Isn’t it too much typing? Instead, you will add a category to NSString that does this work for you.
First create a project, call it Category, and add a new objective-c class file calls NSString+IsValidEmail.
Note: In this tutorial, I will include a regular expression that some of you will not understand. To learn more about regular expression, you can go here: http://en.wikipedia.org/wiki/
Regular_expression
NSString+IsValidEmail.h
The declaration for the category looks a lot like creating a new class:
#import <Foundation/Foundation.h>
@interface NSString (IsValidEmail)
+ (BOOL)isValidEmail:(NSString *)email;
@end
Notice the @interface
declaration: @interface NSString (IsValidEmail)
. First, an existing class followed by the name of the category inside the parenthesis. This means that the category is called IsValidEmail and it adds method to the NSString
.
Note: You can add as many categories to a class as you want as long as the category names are unique
You indicate the class you’re putting the category onto (NSString
) and the class name (IsValidEmail
), and you list the methods you’re adding, followed by the end. The same as the class declaration, you can create an instance method and a class method.
Note: Instance methods are associated with an object, while class methods are associated with a class. Instance method is
You can’t add variables in a category method, so there is no instance variable section as there is with a class declaration. As you can see, we used “+” instead of “–“, meaning we used the class method declaration instead of the instance method declaration.
NSString+IsValidEmail.n
The implementation code of the category looks a lot like creating an implementation of a class:
#import "NSString+IsValidEmail.h"
@implementation NSString (IsValidEmail)
+ (BOOL)isValidEmail:(NSString *)email
{
NSString *regex = @"[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}";
NSPredicate *validator = [NSPredicate predicateWithFormat: @"SELF MATCHES %@", regex];
return [validator evaluateWithObject: email];
}
@end
All of us know that when programming in Objective-C, we create an @interface
and an @implementation
file. You declare the method in the @interface
file and implement the method in the @implementation
file.
Like the interface file for the category, the implementation file has the names of the class and category:
// NSString+IsValidEmail.h
@interface NSString (IsValidEmail)
// NSString+IsValidEmail.m
@implementation NSString (IsValidEmail)
Now, what the code do is create a string holding the regular expression, then create an NSPredicate
that uses predicateWithFormat:
method. SELF
represents the object being evaluated, MATCHES
compare the left hand expression and the right hand expression using a regex-style comparison according to ICU v3. Then, we use an instance method called evaluateWithObject:
to evaluate the email
and return the value true or 1 if it’s valid, and false or 0 if it’s not.
The code in action
Now, let’s try our code:
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad
{
NSLog(@"Is email valid? %i", [NSString isValidEmail: @"[email protected]"]);
if ([NSString isValidEmail: @"[email protected]"])
{
NSLog(@"YES");
}
else
{
NSLog(@"NO");
}
}
The code should display a result similar to this:
2011-03-26 22:22:21.418 Category[1418:207] Is email valid? 1
2011-03-26 22:22:21.420 Category[1418:207] YES
This means that the email string is valid.
Limitations
You might already know that categories have limitations as well. First is that you can’t create variables to a class. Second concerns naming of category. When 2 methods (category and a class method) has the same name, the category wins and it is the one will be used. Meaning the category will replace the original losing the class method. Some programmers add a prefix to their category to make sure there won’t be any conflict.
Wrapping things up
Categories are used mainly for three purposes: splitting a class’s implementation across multiple files or frameworks, forward reference for private methods, and adding informal protocols.
You now have a basic understanding on how the categories work. Just make sure to know when to use a category, consider if a category is really the best choice for what you’re doing.
Leave a Reply