协议
Objective-C的另一个扩展称为协议,它非常象Java中的接口。两者都是通过一个简单的方法声明列表发布一个接口,任何类都可以选择实现。协议中的方法通过其它类实例发送的消息来进行调用。
协议的主要价值和类别一样,在于它可以作为子类化的又一个选择。它们带来了C++多重继承的一些优点,使接口(如果不是实现的话)可以得到共享。协议是一个类在声明接口的同时隐藏自身的一种方式。接口可以暴露一个类提供的所有(通常是这种情况)或部分服务。类层次中的其它类都可以通过实现协议中的方法来访问协议发布的服务,不一定和协议类有继承关系(甚至不一定具有相同的根类)。通过协议,一个类即使对另一个类的身份(也就是类的类型)一无所知,也可以和它进行由协议定义的特定目的的交流。
有两种类型的协议:正式和非正式协议。
①非正式协议:非正式协议在"类别(Category)"部分中已经简单介绍了,它们是NSObject类中定义的类别。因此每个以NSObject为根类的对象(和类对象)都隐式采纳了类别中发布的接口。 与正式协议不同,一个类不必实现非正式协议中的每个方法,而是只实现它感兴趣的方法就可以了。为了使非正式协议正确工作,声明非正式协议的类在向某个目标对象发送协议消息之前,必须首先向它发送respondsToSelector:消息并得到肯定的回答(如果目标对象没有实现相应的方法,则产生一个运行时例外)。在可选协议方法被引入Objective-C 2.0之前,非正式协议实质上是Foundation和AppKit 类用于实现委托的方式。
②正式协议:
正式协议声明一个方法列表,协议采纳者需要实现表中所有方法。正式协议有特殊的声明、采纳以及类型检查语法。您可以使用@required
和@optional
关键字指定方法必须实现或可选实现。子类将会继承其祖先类所采用的正式协议。一个正式协议也可以采纳其他的协议。
- 提供者(通常是一个类)声明正式的协议。
- 客户类采纳正式协议,表示客户类同意实现协议中所有的方法。
- 如果一个类采纳某协议或者是从采纳该协议的类派生出来的(协议可以被子类继承),则可以说该类遵循该协议。
一.正式协议(Protocol)
在Objective-C中,声明和采纳协议都有自己的语法。协议的声明必须使用编译导向符@protocol。Objective C 2.0之后加入新的语法特性@required和@optional。
1.1声明协议
@protocol ProtocolName
//method declarations
@end
示例如下:
//NSCopying协议
@protocol NSCopying
- (id)copyWithZone:(NSZone*) zone;
@end
//NSCoding协议
@protocol NSCoding
- (void)encodeWithCoder:(NSCoder *)aCoder;
- (id)initWithCoder:(NSCoder *)aDecoder;
@end
1.2 类声明中采用协议
如果一个类要采纳某个协议,需要在在@interface导向符后、紧接着超类的位置上指定协议的名称,并包含在尖括号中。一个类可以采纳多个协议,不同的协议之间用逗号分隔。
@interface Car:NSObject<NSCopying>
{
//instance variables
}
//methods
@end//Car
//采用多个协议
@interface Car:NSObject<NSCopying,NSCoding,ProtocolName1,...>
{
//instance variables
}
//methods
@end//Car
1.3 Objective-C 2.0增加了两个新的协议修饰符:@optionnal和@required,默认是@required
1.4 检查一个类是否遵循特定的协议(conformsToProtocol:)
Objective-C通过类遵循的协议和类继承的超类来定义类的类型。您可以通过发送conformsToProtocol:消息来检查一个类是否遵循特定的协议:
if ([anObject conformsToProtocol:@protocol(MyProtocol)]){
// do something appropriate
}
1.5 协议和数据类型
你可以在使用的数据类型中为实例变量和方法参数指定协议名称。这样您就可以通过编译器来得到另一个级别的类型检查,这种检查比较抽象,因为它不和特定的实现相关联。您可以使用与协议采纳相同的语法规则,即把协议的名称放在尖括号中,通过这种语法可以在类型中指定遵循的协议。您常常会看到在这些声明中使用了动态对象类型id,例如:
- (void)draggingEnded:(id <NSDraggingInfo>)sender;
这里,参数中引用的对象可以是任意类型的类,但是必须遵循NSDraggingInfo协议。
除了目前为止已经提到的协议之外,Cocoa还提供了几个协议的例子。一个有趣的例子就是NSObject协议。可以想象得到的是,NSObject类采纳了这个协议,还有一个根类—NSProxy—也采纳了这个协议。通过这个协议,NSProxy类可以和Objective-C运行环境的一部分进行交互,包括引用计数、内省、和对象行为的其它基础部分。
正式协议有其自己的限制。如果协议声明的方法列表随着时间而增长,协议的采纳者就会不再遵循该协议。因此,Cocoa中的正式协议被用于稳定的方法集合,比如NSCopying和NSCoding。如果您预期协议方法会增多,则可以声明为非正式协议,而不是正式协议。