Objective-C基础教程学习笔记(十三)协议

正式协议

与非正式协议一样,正式协议是一个命名的方法列表。但与非正式协议不同的是,正式协议要求显式地采用协议。采用协议的办法是在类的@interface声明中列出协议的名称。此时,你的类遵守该协议。采用协议意味着你承诺实现该协议的所有方法。否则,编译器会通过生成警告来提醒你。

正式协议就像Java的接口一样。事实上,Objective-C的协议正是受了Java接口的启发。

 

声明协议

下面我们看由Cocoa声明的一个协议--NSCopying。如果你采用了NSCopying协议,你的对象将知道如何复制自己:

@protocol NSCopying

- (id) copyWithZone:(NSZone *) zone;

@end

声明协议的语法看起来与声明类或类别的语法有点像。不过这里不是使用@interface,而是使用@protocol告诉编译器:“下面将是一个新的正式协议。”@protocol之后是协议名称,协议名称必须唯一。

然后是一个方法声明列表,协议的每个采用者都必须实现这些方法。协议声明以@end结束。使用协议不引入新的实例变量。

我们来看另一个例子。下面是Cocoa的NSCoding协议:

@protocol NSCoding

-(void) endoceWithCoder:(NSCoder *) aCoder;

- (id) initWithCoder:(NSCoder *) aDecoder;

@end

当某个类采用NSCoding协议时,意味着该类承诺实现这两个方法。encodeWithCoder:方法用于接受对象的实例变量并将其转换为NSCoder类的对象。initWithCoder:方法从NSCoder类的对象中提取经过转换的冻结的(freeze-dried)实例变量并使用它们初始化一个新对象。这两个方法总是成对实现的。如果你从来不将一个对象转换为另一个新对象,那么对该对象进行编码是毫无意义的。如果你从不对对象进行编码,那么你也无法创建一个新对象。

 

采用协议

要采用某个协议,你可以在类的声明中列出该协议的名称,并用尖括号将协议名称括起来。例如,Car类要采用NSCopying协议,则其类声明像下面这样:

@interface Car:NSObject <NSCopying>{

// instance variables

// methods

@end //Car

如果Car类要同时采用NSCopying和NSCoding这两个协议,则类声明如下:

@interface Car:NSObject <NSCopying,NSCoding>{

//instance variables

}

// methods

@end //Car

你可以按任意顺序列出这些协议,没有什么影响。

采用某个协议,相当于给阅读类声明的编程人员发送一条消息,表明该类的对象可以完成两个非常重要的操作:一是能够对自身进行编码或解码,二是能够复制自身。

 

协议和数据类型

你可以在使用的数据类型中为实例变量和方法参数指定协议名称。这样,你可以经Objective-C的编译器提供更多一点的信息,从而有助于检查你代码中的错误。

id类型表示一个可以指向任何类型的对象的指针,它是一个泛型对象类型。你可以将任何对象赋值给一个id类型的变量,也可以将一个id类型的变量赋值给任何类型的对象指针。如果一个用尖括号括起来的协议名称跟随在id之后,则编译器将知道你期望任意类型的对象,只要其遵守该协议。

 

Objective-C2.0新特性

Objective-C2.0增加了两个新的协议修饰符:@optional 和 @required 。早期版本的Objective-C要求必须实现该协议的所有方法。Objective-C2.0则不必。

 

@protocol BaseballPlayer

-(void) drawHugeSalary;

@optional

-(void) slideHome;

-(void) catchBall;

-(void) throwBall;

@required

-(void) swingBat;

@end //BaseballPlayer

因此,一个采用BaseballPlayer协议的类有两个要求实现的方法:-drawHugeSalary和-swingBat,还有3个可选择实现的方法:slideHome,catchBall和throwBall。

非正式协议似乎就可以胜任,为什么 苹果公司还要这样设计呢?这是Cocoa提供的又一利器,可以用来在类声明和方法声明中明确表达我们的意图。如果,你在一个头文件中看到下面这样的代码:

@interface CalRipken:Person <BaseballPlayer>

你可以立即看出我们正在处理BaseballPlayer类的对象。如果使用非正式协议,就无法表达这些信息。同样,你可以使用协议修饰方法的参数:

-(void) draft:(Person<BaseballPlayer>);

这行代码明确指出了应该选拔什么样的人参加棒球比赛。

posted @ 2012-10-15 10:07  TQ.CH  阅读(394)  评论(0编辑  收藏  举报