类型检查
对象的类型声明可以被扩展到正式的协议。这样协议从另外的层面为编译器提供了进行类型检查的依据。这个层面更加抽象,因为他不涉及具体的实现。在类型声明的时候,协议名称位于类型名称后面的尖括号中。如:
- (id <Formatting> )formattingService;
id<MyXMLSupport> anObject;
正如静态类型使得编译器能够进行基于类继承关系的类型检查一样,这种协议类型使得编译器能够基于遵守协议来进行类型检查。
例如,如果Formatter是一个抽象类,如下声明:
Formatter *anObject;
将把所有Formatter派生类的对象分为一组,以便编译器在对这种类型的变量赋值时进行类型检查。类似地:
id <Formatting> anObject;
将把所有遵守Formatting协议的对象分为一组。这种分组和这些对象对象在继承关系图上的位置无关。此时编译器就可以进行类型检查以确保只有遵守了该协议的变量才能被赋值给这种类型的变量。
上述两种情况都是把具有相似性的对象划分到相同的组中,不管是根据他们在继承图上的位置,还是个根据他们含有的相同方法集。
上述两个方式还可以合并为:
Formatter <Formatting> *anObject;
协议不被用来对类对象进行转换。只有实例可以被静态地转换为协议,正如只有实例可以被静态地转换为类一样。(然而,在运行时,类和实例都可以响应conformsToProtocol:消息。)
协议遵守协议
协议也可以向类那样遵守别的协议,其语法如下:@protocol 协议名称 <协议列表>
其中,尖括号中列出的所有协议都将会成为该协议(协议名称指示的协议)的一部分。例如:
@protocol Paging <Formatting>
这样,任何遵守了Paging协议的对象同时都是遵守Formatting协议的。我们把这种机制叫做协议的合并。
之前我们讲过当一个类采纳了某个协议,该类就必须实现该协议中所有的required方法。除此之外,该类还必须遵守该协议合并的其他协议。如果此时,被合并的协议遵守了别的协议,那么该类也必须遵守这些协议。依此类推。对于一个类来说,有以下两种方式来遵守一个合并的协议:
● 实现协议中声明的方法
● 继承那些遵守了这些协议的,并实现了这些方法的类
例如,假设Pager类采纳了Paging协议,其声明如下:
@interface Pager : NSObject <Paging>
那么类Pager就必须实现Paging协议中的所有required的方法。这其中就包括了Paging协议合并的Formatting协议中的required方法。Pager类是通过采纳Paging协议间接地采纳了Formatting协议的。
另一方面,如果Pager是Formatter类的派生类(Formatter类已经独立地采纳了Formatting协议),声明如下:
@interface Pager : Formatter <Paging>
那么Pager类就必须实现Pageing协议中的那些required方法,但可以不实现Formatting协议中的required方法。Pager类从Formatter类中继承了那些遵守Formatting协议的方法。
注意:类可以以非正式的形式下遵守某个协议,只要类中实现了该协议声明中的方法即可。
引用别的协议
在一些复杂的应用程序中,我们偶尔会发现如下的代码: @import "B.h"
@protocol A
- foo:( id <B> )anObject;
@end
其中B的声明如下:
#import "A.h"
@protocol B
- bar:(id <A>)anObject;
@end
此时形成了递归式的引用,两个文件都不能正确地被编译。为了突破这种递归式的相互引用,我们必须使用@protocol命令字来对所需的协议进行一个前置的声明,而不能引入定义其的接口文件,如下所示:
@protocol B;
@protocol A
- foo : (id <B>)anObject;
@end
这种使用@protocol命令字的方式只是告诉编译器协议B是在后面才会被定义的协议,并没有引入定义B协议的接口文件。