第九章 分类和协议
9.1 分类
通过分类(category)可以以模块的方式向现有的类添加方法。 它提供了一种简单的方式, 用它可以将类
的定义模块化到相关方法的组或分类中。它还提供了扩展现有类定义的简便方式,并且不必访问类的源代 码,也无需创建子类。
对于 Fraction 类,除了将两个分数相加的 add:方法外,还想要拥有将两个分数相减、相乘和相除的方 法。
//**********************************************************************************************// // Fraction 类声明
#import<Foundation/Foundation.h>
@interface Fraction : NSObject
{
int numerator;
int denominator; }
@property int numerator, deniminator; -(void) setTo:(int) n over: (int) d; -(Fraction *) add: (Fraction *) f; -(void) print;
// 声明分数的 加法函数
@end //*********************************************************************************************//
现在,从接口部分删除 add:方法,并将其添加到新分类,同时添加其他三种要实现的数学运算。看一
下新 MathOps 分类的接口部分。 //*********************************************************************************************//
#import<Foundation/Foundation.h>
#import "Fraction.h"
@interface Fraction (MathOps)
-(Fraction *) add: (Fraction *) f; // 加法函数
-(Fraction *) mul: (Fraction *) f;
-(Fraction *) sub: (Fraction *) f;
-(Fraction *) div: (Fraction *) f;
// 乘法函数 // 减法函数 // 除法函数
@end //*********************************************************************************************//
// #inport "Fraction.h" 这里既是分类接口部分的定义,也是对现有接口部分的扩展,所以必须包括原始接口部分 // @interface Fraction (MathOps) 告诉编译器正在为 Fraction 类定义新的分类,名称为 MathOps。
可以在一个实现文件中定义 Fraction.h 接口部分中的所有方法,以及 MathOps 分类中的所有方法。
也可以在单独的实现部分定义分类的方法。在这种情况下,这些方法的实现部分还必须找出方法所属 的分类。 与接口部分一样, 通过将分类名称括在类名称之后的圆括号中来确定方法所属的分类, 如下示:所
@implementation Fraction (MathOps) // code for category methods
... @end
关于分类的一些注意事项
A、 尽管分类可以访问原始类的实例变量, 但是它不能添加自身的任何变量。 如果需要添加量,变
可以考虑创建子类。
B、 分类可以重载该类中的另一个方法,但是通常认为这种做法不可取。因为,重载之后,再不
能访问原来的方法。
C、 可以拥有很多分类。
D、 和一般接口部分不同的是,不必实现分类中的所有方法。这对于程序扩展很有用,可以在该
分类中声明所有方法,然后在一段时间之后才实现它。
E、 通过使用分类添加新方法来扩展类不仅会影响这个类,同时也会影响它的所有子类。
9.2 协议 协议的声明类似于类接口的声明,有一点不同的是,协议没有父类,并且不能定义成员变量。下面的
例子演示了只有一个方法的协议的声明:
@protocol MyProtocol
- (void)myProtocolMethod; @end
协议是多个类共享的一个方法列表, 协议中列出的方法没有相应的实现。 如果一个类采用MyProtocol 协议,则必须实现名为 myProtocolMethod 的方法。
通过在@interface 行的一对尖括号<...>内列出协议名称,可以告知编译器你正在采用一个协议。这项 协议的名称放在类名和它的父类名称之后,如下所示:
@interface AddressBook: NSObject <myProtocol>
这说明, AddressBook 是父类为 AddressBook 的对象,并且它遵守 myProtocolMethod 协议。在 AddressBook 的实现部分,编译器期望找到定义的myProtocolMethod 方法。
如果采用多项协议,只需把它们都列在尖括号中,用逗号分开:
@interfaceAddressBook: NSObject<myProtocol,yourProtocol>
以上代码告知编译器 AddressBook 类采用 myProtocolMethod 和 yourProtocolMethod 协议。这次,编
译器将期望在 AddressBook 的实现部分看到为这些协议列出的所有方法的实现。
有关协议的注意事项:
A、如果一个类遵守某项协议,那么它的子类也遵守该协议。
B、协议不引用任何类,它是无类的(classless) 。任何类都可以遵守某项协议。
C、通过在类型名称之后的尖括号中添加协议名称,可以借助编译器的帮助来检查变量的一致性,如
下:
id <Drawing> currentObject;
这告知编译器 currentObject 将包含遵守 Drawing 协议的对象。 如果向currentObject 指派静态类型的对
象,这个对象不遵守 Drawing 协议,编译器将给出 warning。
再次提到 id 类型,如果向 currentObject 指派一个 id 变量,不会产生这条消息,因为编译器不知道存
储在 id 变量中的对象是否遵守 Drawing 协议。 D、如果这个变量保存的对象遵守多项协议,则可以列出多项协议,如下:
id <Drawing, Drawing 1> currentObject; E、定义一项协议时,可以扩展现有协议的定义。以下协议
@protocol Drawing3D <Drawing>
说明 Drawing3D 协议也采用了 Drawing 协议。因此采用 Drawing3D 协议的类都必须实现此协议列出
的方法,以及 Drawing 协议的方法。 F、分类也可以采用一项协议,如:
@interface Fraction (stuff) <NSCopying, NSCoding>
此处,Fraction 拥有一个分类 stuff,这个分类采用了 NSCopying 和 NSCoding 协议。