Objective-c的分类和协议

13年前
在Objective-c中使用分类和协议的目的:
1,使用分类(category)以模块的方式向类添加方法.
2,创建标准化的方法类表供其他人实现.

分类:
分类产生的背景,一下举几个例子
1,有时候在处理类定义时,可能想要为其添加一些新方法.(思考.在java中这种情况下是怎么做的呢?).
2,假如你参与了一个大型程序设计项目,并且作为项目的一部分,正在定义一个新类,新类中包含许多方法.你的任务是:为该类编写处理文件系统的方法.其他项目成员的任务负责以下方法:创建和初始化该类实例,对该类中的对象执行操作以及在屏幕上绘制该类对象的表示.(思考.在java中这种情况下是怎么做的呢?).

针对以上所有情况的在Objective-C中使用的解决方案是:分类.
分类提供了一种简单的方式,用它可以将类的定义模块化到 相关方法的组 或者 分类中.分类还提供了一种扩展现有类定义的简便方式,并且不必访问类的源代码.也无需创建子类.

例如:
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;
然后,从接口部分删除add:方法,并将其添加到新分类,同时添加其他三种要实现的数学运算.新MathOps分类的接口部分应该如下所示.
#import "Fraction.h"
#interface Fraction (MathOps)
-(Fraction *) add: (Fraction *) f;
-(Fraction *) mul: (Fraction *) f;
-(Fraction *) sub: (Fraction *) f;
-(Fraction *) div: (Fraction *) f;
@end;
 注意:这既是接口部分的定义,也是现有接口部分的扩展.因此必须包括原始接口部分.这样编译器就知道Fraction类.
#interface Fraction(MathOps):这句代码告诉编译器你正在为Fraction编写新的分类.而且新的分类的名称是MathOps.在这里没有列出实例变量,因为在以前定义的接口.以上扩展接口的作用是:告诉编译器,你正在MathOps分类下为名为Fraction类添加扩展.
可以将所有方法的定义放在一个实现部分,也就是,可以在一个实现文件中定义Fraction.h接口中的所有方法和MathOps分类中的所有方法.也可以在单独的实现部分定义分类的方法.在第二种情况下,这些方法的实现部分还必须找出方法所属的分类.和接口部分一样,通过将类名称扩在类名称之后的圆括号内类确定方法所属的分类.
@implementation Fraction(MathOps)
//code for category methods
@end;

一下是关于分类需要注意的一些注意事项
1.尽管分类可以访问原始类的实例变量,但是它不能添加自身的实力变量,如果需要添加变量,可以考虑创建子类.
2.分类可以重载类中的另一个方法,但是通常不这样做,这种做法是拙劣的设计习惯.重载了一个方法之后,再也不能访问原来的方法.
3.可以拥有多个分类,如果一个方法定义在多个分类中,该语句不会执行指定使用哪个分类.
4,和一般接口不同的是,不必实现分类中的所有方法.
5.通过使用分类添加新方法来扩展不仅会影响这个类,还会影响其所有子类.

协议:

协议是多个类共享一个方法列表.协议中列出的类没有响应的实现,有其他人来实现.
协议使用指定的名称定义一组多少有点相关的方法.这些方法通常有文档说明.所以你知道他们将如何执行.因此如果需要,可以在自己的类中定义他们的实现.
如果决定实现特定协议的所有方法,也就意味着要遵守这项协议.
定义一个协议:下面是一个例子
@protocol NSCopying
- (id)copyWithZone: (NSZone *) zone;
@end;
如果你的类采用了NSCopy协议,则必须遵守实现名为copyWithZone的方法.通过在@interface行的一对<>内列出协议名
可以告诉编译器你正在使用一个协议.这项协议的名称放在类名和它的父类名称之后.
@interface AddressBook: NSObject <NSCopy>如果你的类实现了多项协议,协议之间用逗号分开.
协议不引用任何类型,它是无类的.任何类都可以遵守Drawing协议.
可以使用ConformsToProtocal:方法检查一个对象是否遵守某项协议.
例如有一个名为currentObject的对象,并且想要查看它时候是遵循Drawing协议.可以向他放松绘图消息
id currentObjec;
if([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两个协议.
和类名称一样,协议名称也是唯一的.
 
 
非正式协议:
分正式协议实际上就是一个分类.列出了一组方法,但是并没有实现他们.每个人都继承相同的跟对象,因此非正式分来通常是为跟类定义的.又是非正式协议又叫做抽象协议.
 
声明分正式协议的类并不自己实现这些方法,并且选择实现这些方法的子类需要在他的接口部分重新声明这些方法.同时还要是实现这些方法中的一个或多个,
 
 
 
合成对象:
你已经学习了通过派生子类和分类技术类扩展类定义的集中方法.还有一项涉及定义一个包含其他类的一个或者多个对象的技术.
@interface Square :NSObject
{
Rectangle * rect;
}
-(int) setSide:(int)s;
@end