2.6 类别(cIass category)
类别是一种强大的功能,它允许开发者不用子类化就可以为一个既有的类来添加方法。类别可以帮助将类的实现分散到不同文件或者多个不同的框架中。在读iOS许多控件类的源文件时可以看到很多使用类别的地方。
在Xcode中创建类别非常容易,下面的例子中将为NSString类通过类别添加一个检查E-mail地址是否合法的方法。为此,通过Xcode首先创建一个Command Line Tool类型的ClassCategory 项目,创建完以后打开新建文件向导,如图2-26所示,在创建新文件向导的左侧选择OS X下方的Cocoa,右侧的文件模板列表中就可以找到Objective-C category即类别模板。
如果是创建iOS应用,同样,也可以在图2-26所示的向导窗口左侧选择iOS下方的Cocoa Touch,右侧的文件模板列表中也可以找到同样的类别模板。
图2-26 创建类别
接下来单击“Next”按钮,在类别信息输入窗口中的Category文本框中输入类别的名字NSStringUtility,顾名思义,就是NSString类的工具方法。在Category on下拉列表框中选择NSString,表明是为NSString类创建类别,如图2-27所示。
图2-27 为NSString类创建类别
单击“Next”按钮选择保存路径,单击创建后可以在Xcode左侧的项目导航栏窗口中看到Xcode创建了两个类相关文件,类名里包含NSString及NSStringUtility用来表明这是为NSString类创建的一个类别文件,Xcode通过这种方式让类名有意义的同时,也方便进行类名的管理,如图2-28所示。
图2-28 项目导航栏窗口
打开NSString+NSStringUtility.h头文件,修改代码如下:
1: #import <Foundation/Foundation.h> 2: 3: @interface NSString (NSStringUtility) 4: 5: -(BOOL)validateEmail; 6: 7: @end
头文件并不复杂,留意第3行的最后在NSString的后面添加了一对括号,括号中就是类别的名字,这是类别与一般类定义唯一不同的地方,Objective-C正是通过这种方式来识别类别的。接着打开NSString+NSStringUtility.m类别的实现文件,修改代码如下:
1: #import "NSString+NSStringUtility.h"
2:
3: @implementation NSString (NSStringUtility)
4:
5: -(BOOL)validateEmail
6: {
7: BOOL stricterFilter = YES;
8: NSString *stricterFilterString =
@"[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}]";
9: NSString *laxString = @".+@.+\\.[A-Za-z]{2}[A-Za-z]*";
10: NSString *emailRegex = stricterFilter ? stricterFilterString : laxString;
11: NSPredicate *emailTest = [NSPredicate predicateWithFormat:@"SELF MATCHES %@",
emailRegex];
12: return [emailTest evaluateWithObject:self];
13: }
14:
15: @end
上面的类别代码主要从第5行开始添加了validateEmail方法的具体实现,其中使用了正则表达式及NSPredicate类,NSPredicate类通过正则表达式来验证字符串是否符合邮箱地址的格式。留意第12行调用了NSPredicate的方法evaluateWithObject:方法,该方法中将self也就是NSString字符串本身传入以做校验。不理解正则表达式或NSPredicate方法也没有关系,最重要的是理解我们为 NSString 类创建了类别,这个类别中包含一个方法validateEmail,该方法将用来验证NSString对象是否符合E-mail的格式。
接下来修改main.m文件,添加测试代码来测试validateEmail方法:
1: #import <Foundation/Foundation.h> 2: #import "NSString+NSStringUtility.h" 3: 4: int main(int argc, const char * argv[]) 5: { 6: @autoreleasepool 7: { 8: NSString *strEmail = @"test@host"; 9: if([strEmail ValidateEmail]) 10: NSLog(@"%@是一个合法的邮箱地址", strEmail); 11: else 12: NSLog(@"%@不是一个合法的邮箱地址", strEmail); 13: } 14: return 0; 15:}
上面代码的第2行导入了我们定义的类别文件,第6行第1次保留了Xcode自动添加的@autoreleasepool,这个标记是自动释放池,意味着下面大括号以内的对象内存将自动释放。这是因为Xcode认为NSPredicate对象存在内存泄漏的风险,运行时将提示内存警告信息,加上@autoreleasepool标记可以避免内存警告信息。
代码的第8行定义了一个不合格格式的NSString对象strEmail,然后调用类别中定义的方法validateEmail来判断strEmail对象是否为合法的E-mail地址,运行结果如图2-29所示。
图2-29 非法邮箱地址错误日志
上面的例子中,如果将strEmail对象的值改为test@host.com后再运行,就会收到验证通过的提示信息,如图2-30所示。
图2-30 合法邮箱地址日志
类别的定义并不复杂,在后面iOS应用开发的代码中,将有机会利用到类别。