通过创建Category可以为现有的类添加新方法。为什么不派生子类?因为Objective-C中有很多类本质是类簇,为这样的类创建子类会很困难。或者我们可以创建子类,但代码中正在使用的某些工具集或库可能无法处理新子类的对象。
使用Category可以为任何类添加新的方法,甚至那些没有源代码的类。
1,使用分类(category)以模块的方式向类添加方法.
2,创建标准化的方法类表供其他人实现.
11.1:分类
使用场景:
1,有时候在处理类定义时,可能想要为其添加一些新方法.
2,假如你参与了一个大型程序设计项目,并且作为项目的一部分,正在定义一个新类,新类中包含许多方法.你的任务是:为该类编写处理文件系统的方法.其他项目成员的任务负责以下方法:创建和初始化该类实例,对该类中的对象执行操作以及在屏幕上绘制该类对象的表示.
3,假如你已经知道如何使库中的类(例如fundation的数组类,NSArray),并且意识到你希望该类实现了一个或者多个方法.当然,你可以编写NSArray类的新子类并实现新方法.
针对以上所有情况的使用的解决方案可以用:分类.
分类提供了一种简单的方式,用它可以将类的定义模块化到 相关方法的组或者分类中.分类还提供了一种扩展现有类定义的简便方式,并且不必访问类的源代码.也无需创建子类.
Fraction原始代码
#import <Foundation/Foundation.h>
//define the Fraction class
@interface Fraction : NSObject
{
//声明实例变量
int numerator;
int denominator;
}
//将实例变量转换为属性
@property int numerator,denominator;
//定义实例方法
-(void) setTo: (int) n over: (int) d;
-(Fraction *) add: (Fraction *) fraction;
-(void) reduce;
-(double) convertToNumber;
-(void) print;
@end;
#import "Fraction.h"
#interface Fraction (MathOps)
-(Fraction *) add: (Fraction *) f;
-(Fraction *) mul: (Fraction *) f;
-(Fraction *) sub: (Fraction *) f;
-(Fraction *) div: (Fraction *) f;
@end;
#interface Fraction(MathOps):这句代码告诉编译器你正在为Fraction编写新的分类.而且新的分类的名称是MathOps.在这里没有列出实 例变量,因为在以前定义的接口部分中已经这样做了.实际上如果再次列出的话,将接收到编译器发出的语法错误.
以上扩展接口的作用是:告诉编译器,你正在MathOps分类下为名为Fraction类添加扩展.
可以将所有方法的定义放在一个实现部分,也就是,可以在一个实现文件中定义Fraction.h接口中的所有方法和 MathOps分类中的所有方法.也可以在单独的实现部分定义分类的方法.在第二种情况下,这些方法的实现部分还必须找出方法所属的分类.和接口部分一 样,通过将类名称扩在类名称之后的圆括号内类确定方法所属的分类.
@implementation Fraction(MathOps)
//code for category methods
@end;
关于分类的一些注意事项:
1.尽管分类可以访问原始类的实例变量,但是它不能添加自身的实力变量,如果需要添加变量,可以考虑创建子类.
2.分类可以重载类中的另一个方法,但是通常不这样做,这种做法是拙劣的设计习惯.重载了一个方法之后,再也不能访问原来的方法.
3.可以拥有多个分类,如果一个方法定义在多个分类中,该语句不会执行指定使用哪个分类.
4,和一般接口不同的是,不必实现分类中的所有方法.
5.通过使用分类添加新方法来扩展不仅会影响这个类,还会影响其所有子类.
11.2:协议:
协议是多个类共享一个方法列表.协议中列出的类没有响应的实现,有其他人来实现.
协议使用指定的名称定义一组多少有点相关的方法.这些方法通常有文档说明.所以你知道他们将如何执行.因此如果需要,可以在自己的类中定义他们的实现.
如果决定实现特定协议的所有方法,也就意味着要遵守这项协议.
定义一个协议:下面是一个例子
@protocol NSCopying
- (id)copyWithZone: (NSZone *) zone;
@end;
如果你的类采用了NSCopy协议,则必须遵守实现名为copyWithZone的方法.通过在@interface行的一对<>内列出协议名
可以告诉编译器你正在使用一个协议.这项协议的名称放在类名和它的父类名称之后.
@interface AddressBook: NSObject <NSCopy>如果你的类实现了多项协议,协议之间用逗号分开.
协议不引用任何类型,它是无类的.任何类都可以遵守Drawing协议.
可以使用ConformsToProtocal:方法检查一个对象是否遵守某项协议.
例如有一个名为currentObject的对象,并且想要查看它时候是遵循Drawing协议.可以向他放松绘图消息
id currentObjec;
([currentObjec : ConformsToProtocal: @protoco (Drawing) : == YES]){
....//DO SOMETHING;
}
这里使用一个专用的@protocol指令用于获取一个协议名称.并产生一个Protocol对象.
通常在类型名称之后的<>中添加协议名称,可以借助编译器来检查变量的一致性.id <Drawing> currentObject;
这告诉编译器currentObject将包含遵守Drawing协议的对象.如果这个对象遵循多个协议,可以用逗号隔开.
定义一项协议的时候,可以扩展现有的协议.
@protocol Drawwing3D <Drawing>说明Drawwing3D协议也使用了Drawing协议.
最后分类也可以采用一项协议
@interface Fraction (Stuff) <NSCopy,NSCoding>
此处Fraction类拥有一个Stuff分类,这个分类采用了NSCopy,NSCoding两个协议.
和类名称一样,协议名称也是唯一的.
然后,从接口部分删除add:方法,并将其添加到新分类,同时添加其他三种要实现的数学运算.新MathOps分类的接口部分应该如下所示.
注意:这既是接口部分的定义,也是现有接口部分的扩展.因此必须包括原始接口部分.这样编译器就知道Fraction类.