协议就是声明方法的集合,它表示对象的行为。协议与具体的实现无关,它是根据消息来获取对象的一套系统化的方法。如果能够熟练掌握协议的使用,就能够定义出高灵活性,低耦合性的类。
协议的概念
什么是协议
表示对象的作用和行为的方法的集合体就称为协议(prototol)。举个例子,CD播放器,MD播放器,数字音频播放器中播放,停止,跳过等公共功能就是我们所说的协议。只要与这些操作方法相对应,也就是说只要提供了公共协议,无论使用什么样的存储媒体,就都可以播放或停止播放音乐。而对协议来说,每个播放器也可以独立实现各自的功能。
对象的协议
在对象模型化的软件世界中,不同的对象也可能包含相同的方法集合,但通常情况下,这些对象并不是继承关系。
OC中的协议仅仅是声明方法的集合体,实现方法则由各个类自行完成。因此,使用协议的各个类之间是否有继承关系都无关紧要,重要的是如何实现这些方法。
如果类实现了该协议声明的所有方法,我们就说类遵循(conform)该协议。而它的子类实例因为继承关系也拥有了这些协议的方法。当类适用于某个协议时,它的实例也适用于这个协议。
协议也可以被理解成只有声明而没有实现方法和实例变量等的抽象类。
Objective-C中协议的声明
协议的声明
@protocol协议名
声明方法;
…
@end
协议名命名规范于类名相同,即首字母大写,其他字母小写。此外,协议名也可以与已有的类同名。方法的声明中也可以使用属性声明。
协议通常被作为头文件书写,并在类的声明之前导入。
协议的采用
@interface类名:超类名<协议名
> {
声明接口变量;
...
}
声明方法;
...
@end
类的接口声明指定了某个协议,我们称为类采用了该协议。
这样声明时,协议中的方法就被作为了类声明的一部分。因此,在类的接口声明中,就无须再声明这些方法了。协议中的方法在实现文件中也必须要实现。但在超类中已实现的方法就不用再重新实现了。
子类成为该协议适用的类,而子类也可以同时使用别的协议。
一个类中可以同时采用多个协议,在<>括号内,将多个协议名用逗号分隔即可。
即使多个协议中重复包含同一个方法的声明也没有问题。
但是,选择器相同而函数参数和返回值的类型不一样,即签名不同的方法在协议中重复声明时就会出现问题。一个类内不能声明包含同一个选择器的另一个方法,也不能定义多个这样的协议。
而当协议中的方法在某个范畴中实现时,就可以在该范畴中声明采用该协议:
@interface类名(范畴名)<协议名>声明方法;
...
@end
通过此方法还可以在已知类中添加协议的方法实现。
协议的继承
在某个协议中,可以追加另一组方法来产生新的协议,这称为协议的继承。声明方法如下:
@protocol协议名1<协议名2>声明方法
...
@end
而且协议还可以有多个继承源,增加多个继承源时在<>内将多个协议名用逗号分隔即可。
指定协议的类型声明
声明某个对象适用于某个协议,例如声明obj适用于协议S时,就可以采用如下方式定义:
id obj;
该声明也可以作为临时参数。
在声明指定协议的类型时编译器会对类型进行静态检查。但需注意的是,在运行时并不会对类型进行动态检查。
类是否适用于协议,与每个方法是否得到了实现无关,而是根据在接口文件中是否声明了采用协议来判断。
不仅是id类型,具体的类名和范畴的组合也可以被当成类型来使用。如下所示:
- (void)setAlternativeView:](NSView *)aView;
在此例中,参数aView不仅是类NSView的实例,还使用了范畴或继承,同时还是协议Clickable适用的对象,此例中静态说明了这些特性。
如果一个对象使用了协议,那么在指定该对象的类时,类对象只要能适用于指定的协议就行,而不用管它是什么类的对象。这样就可以编写出不依赖于具体的类的实现的,高灵活性的代码。
代码中只关注协议和抽象类,而没有具体类名的对象称为匿名对象(anonymous object)。例如:
[ Person new ];
没有变量名。
协议的前置声明
如果只在头文件的类型声明中使用协议,可以指定前向引用。例如:
@protocol 类名;
协议适用性检查
使用编译器命令符@protocol()后,就可以获得表示指定协议数据的指针。在运行时可以动态地检查对象是否适用于某个协议。@protoco()参数中包含类型(Protocol *),可以带入变量。
+ (BOOL)conformsToProtocol:(Protocol *)aProtocol//aProtocol参数指定的协议和类适用时,返回YES。
- (
BOOL)conformsToProtocol:(Protocol *)aProtocol//aProtocol参数指定的协议和类适用时,返回YES。
检查对象obj是否适用于协议NSLocking,可以用如下方式:
if([obj conformsToProtocol:@protocol(NSLocking)])...
必选功能和可选功能
在协议声明中,编译器命令符@optional和@required可用来设定其后出现的方法是可选的还是必选的。而@optional和@required命令符在声明中以什么样的顺序出现以及出现多少次都可以。如果声明中没用特殊指定,那么就默认为@required,表示方法是必选的。
由于采用协议的类可以不实现可选方法,因此就需要动态的检查方法是否可用。
非正式协议
什么是非正式协议
将一组方法声明为NSObject的范畴,就称为非正式协议(informal protocol),或称为简化协议。
非正式协议只是作为范畴进行声明,并没有实现。范畴中声明的方法即使没有实现,也可以编译或执行,但是发送消息时会出现运行时错误。
如果要检查非正式协议中的方法是否已经实现,只能对每个方法调用respondsToSelector:
总结非正式协议的相关概念:
非正式协议被声明为NSObject类的范畴
非正式协议中声明的方法不一定要实现
编译时,不能检查类对非正式协议的适用性
运行时,不能检查类对非正式协议的适用性。只能确认是否实现了每个方法。