创建类别
类别是一种为现有的类添加新方法的方式。可以为任何类添加新的方法,包括那些你没有源代码的类。
模拟:你正在编写一个纵横字谜游戏程序,该程序将接收一系列的字符串,确定每个字符串的长度并将这些字符串的长度存入NSArray或NSDictionary中。你需要先将每个长度包装在一个NSNumber对象中,然后才能将期存入NSArray或NSDictionary中。做多了就会厌烦了吧。相反,你可以为NSString类添加一个类别,该类别可以替你完成这项工作。
声明类别
类别的声明格式与类的声明格式相似:
@interface NSString (NumberConvenience)
-(NSString *) lengthAsNumber;
@end //NumberConvenience
该声明具有两个特点。首先,现有的类位于@interface关键字之后,其后是位于圆括号中的一个新名称。该声明表明,类别的名称是NumberConvenience,而且该类别将向NSString 类中添加方法。换句话说:“我们正在向NSString类添加一个名称为NumberConvenience的类别。”只要保证类别名称的唯一性,你可以向一个类中添加任意多的类别。
其次,可以指定希望向其添加类别的类(上例中的NSString)以及类别的名称(上例中的NumberConenience),而且你还可以列出添加的方法,最后以@end结束。由于不能添加新实例变量,因为与类的声明不同的是,类别的声明中没有实例变量部分。
实现类型
与@interface部分对应的还有一个@implementation部分。
@implementation NSString (NumberConvenience)
-(NSNumber *) lengthAsNumber{
unsigned int length = [self length];
return [NSNumber numberWithUnsignedInt:length];
}//lengthAsNumber
@end //NumberConvenience
这样,任何NSString类的对象都将响应lengthAsNumber消息。
不需要创建NSString类的子类来获得这种行为,类别就可以完成同样的工作。
类别的局限性
类别有两方面的局限性。第一,无法向类中添加新的变量。第二,名称冲突。当发生名称冲突时,类别具有更高的优先级。类别方法将完全取代初始方法,从而无法再使用初始方法。
类别的作用
Cocoa中的类别主要用于3个目的:将类的实现分散到多个不同文件或多个不同框架中,创建对私有方法的前向引用,以及向对象添加非正式协议。
利用类别分散实现
我们可以将类的接口放入头文件中,而将类的实现放入.m文件中。但不能将@implementation分散到多个不同的.m文件中。如果希望将大型的单个类分散到多个不同的.m文件中,可以使用类别来完成这一工作。
以AppKit提供的NSWindow为例。如果查看NSWindow的文档,你将会发现该类有数百个方法。
@interface NSWindow:NSResponder
然后是一大堆的类别,
@interface NSWindow(NSKeyboardUI)
@interface NSWindow(NSToolbarSupport)
@interface NSWindow(NSWindowDelegate)
...
利用类别,所有的键盘用户界面代码都位于一个源文件中,工具栏代码位于一个源文件中,拖放功能位于另一个源文件中。类别还可以将方法组织到逻辑分组中,使编程人员可以更加容易地阅读头文件。
使用类别创建前向引用
编译器遇到你调用对象的某个方法,但是没有找到该方法的声明或定义,编译器会发出错误提示。如果能够先定义一个方法然后再使用它,编译器将会找到你的方法定义,因而不产生警告。
呆要在类别中声明了一个方法,编译器就会表示:“好了,该方法已经存在,如果遇到编程人员使用该方法,我不会提出警告。”实现上,你不必实现那些你不想实现的方法。
非正式协议和委托类别
Cocoa中的类经常使用一种名为委托(delegate)的技术,委托是一种对象,另一个类的对象会要求委托对象执行它的某些操作。例如,当AppKit类的NSApplication启动时,它会询问其委托对象是否应该打开一个无标题窗口。以NSWindow类的对象询问它们自己的委托对象是否应该允许关闭某个窗口。
更常用的是,编写委托对象并将其提供给其它一些对象,通常是提供给Cocoa生成的对象。
委托强调类别的另一种应用:被发送给委托对象的方法可以声明为一个NSObject的类别。通过将这些方法声明为NSObject的类别,NSNetServiceBrowser的实现可以将这些消息之一发送给任何对象,无论这些对象实际上属于哪个类。这也意味着,只要对象实现了委托方法,任何类的对象都可以成为委托对象。
创建一个NSObject的类别称为“创建一个非正式协议”。大家都知道,计算机术语中的“协议”是一组管理通信的规则。非正式协议只是一种表达方式,它表示“这里有一些你可能希望实现的方法,因此你可以使用它们更好地完成工作”。
响应选择器
当试图发送一个对象无法理解的消息时,运行时错误:selector not recognized
选择器是一个方法名称,它以Objective-C运行时使用的特殊方式编码,以快速执行查询。你可以使用@selector()预编译指令指定选择器,其中方法名位于圆括号中。@select(setEngine:)
NSObject提供了一个名为respondsToSelector:方法,该方法询问对象以确定其是否能够响应某个特定的消息。
if([Car respondsToSelector:@selector(setEngine:)]){
NSLog(@”yowza!”);
}
如果Car类的对象能够响应该setEngine:消息,会输出。