声明:本文档仅为个人学习过程中顺手翻译之作,方便开发的同胞借鉴参考。如有觉得译的不好不到位的地方,欢迎指正,将及时做出更正
尽量尊重原文档,因为首次Objective-C,有些地方可能直译了没有注意该语言的专有词,希望指正。如需转载,请注明出处
我的编程环境:
IDE:XCODE4.3.1
OS: MAC OS X 10.7.4
文章来译自:http://developer.apple.com/
协议协议会声明一些可以被其它类实现的方法。协议通常在一下几种情况下很有用:
声明一些期待被实现的方法。
当为了隐藏一个对象的类而给其声明接口时。
为了捕获那些不存在继承关系的类的共同点
声明可以被实现的接口
类和类别接口声明一些方法,它们与一个特定的类联系在以其,这个类需要主要实现的一些方法。正式跟非正式的协议,声明的方法不依赖任何特定的类,但任何类,很多类都可以实现它的这些方法。
协议只是一系列方法的声明,没有类的定义。例如,这些方法用来报告用户对鼠标的操作可以集合在一起,放在协议中:
- (void)mouseDown:(NSEvent *)theEvent;
- (void)mouseDragged:(NSEvent *)theEvent;
- (void)mouseUp:(NSEvent *)theEvent;
任何想要响应鼠标时间的类都可以采用这个协议,并实现它的方法。
协议让方法的声明从类继承的依赖中解脱出来,所以它们可以被用于任何类跟类目所不能够的方式去使用。协议的方法清单会(或者可能会) 在某个地方被实现, 但协议对是究竟是哪个类实现了它们毫无兴趣。它所感兴趣的是是否那个特定的类符合协议的要求—是否它实现了协议声明的那些方法。这样对象就按照类型分类就可以不仅仅基于基本的继承自相同类的产生的共同点,还可以基于基于它们符合相同协议产生的共同点。不相干的继承分支的类可能类型会很像,因为它们都符合相同的协议。
协议可以在面向对象设计中扮演一个很重要的角色,尤其是当一个项目要分散到很多实现者手中或者它包含在别的项目中的对象的时候。 Cocoa 软件大量的使用协议来支持Objective-C 消息的进程间通信.
但是,一个Objective-C 程序没必要使用那么多协议。不像类的定义和消息表达式,协议是可选的。一些Cocoa 框架使用它们,一些不用。这取决于你手头的任务。
用于给别人实现的方法
如果你知道一个对象的类,你可以查看它的接口声明(以及它集成到的类的接口声明) 来找出它是给什么消息进行响应。这些声明通知消息它可以接收。协议也提供了一个方法通知消息它的发送。
通信是双向的;对象发送消息也接收消息。例如,一个对象可能责任委派一个特定的操作给另外的对象,或者它可能偶尔需要简单的请求另外的对象来获取信息。在一些情况下,一个对象可能乐意把它的动作通知其它对象,这样它们就可以采取任何要求的并行措施。
如果你要开发发送器类跟接收器类来作为同一个项目的一部分 (或者如果另外某个人已经想你提供了接收器以及它的接口文件), 这个通信很好协调。发送器只是简单的导入接收器的接口文件。导入的文件声明了发送器在它发送的消息中使用的方法选择器。
然而,如果你要开发一个对象,这个对象要发消息给一个它还没定义的对象—那个你留给其他人来实现的对象—你不必获得接收器的接口文件。你需要另外一种方式来声明你在消息中使用方法,不必实现。协议符合这个要求。 它将类使用的方法通知编译器,也将需要声明的方法通知给其他实现者,以使它们的对象可以跟你的进行协作。
假设,你在开发一个对象,需要通过给另外一个对象发送helpOut: 以及其他消息来请求它的协助. 你提供了一个 assistant 实例变量来为这些消息记录 outlet 并声明了一个配套方法来设置这个实例变量。 这个方法让其他的对象把它们自己注册成你消息的潜在接受者:
- setAssistant:anObject
{
assistant = anObject;
}
然后,只有有消息发送到 assistant, 将会运行一个检查以确保接受者实现了一个它可以响应的方法:
- (BOOL)doWork
{
...
if ( [assistant respondsToSelector:@selector(helpOut:)] ) {
[assistant helpOut:self];
return YES;
}
return NO;
}
因为这次你写的这些代码,你并不知道什么样的对象可能会把它自己注册成assistant, 你只能为 helpOut: 方法声明一个协议; 不用导入实现它的类的接口文件。
为匿名对象声明接口
协议可以用于声明一个匿名对象的方法,一个未知类的对象。一个匿名对象可能代表是一个服务或者处理有限的一组函数,尤其是当只需要该类型的一个对象时。(对象在顶一个应用的架构时扮演着一个基础角色,而且对象在使用前必须初始化,这对于匿名来说可不好)
对象对它们的开发者来说不是匿名的,当然,当它们被开发者提供给别人的时候,它们就是匿名的了。例如下列情形:
有人要提供一个框架或者一套对象给其它人来使用,可以包含没有类名或者接口文件标识的对象。缺少名字和类接口的情况下,用户是无法创建类的接口实例的。取而代之的是,提供者必须给出一个现成的实例。通常来说,在另外一个类的方法来返回一个有用的对象:
id formatter = [receiver formattingService];
这个通过方法返回的对象没有类标识,至少不是提供者想返回的。它要有所用处,提供者就必须愿意标识一些它能响应的消息。通过将这个对象与声明在一个协议中的一系列方法联系起来以完成消息的标识目的。
你可以发送Objective-C 消息给远程对象—在别的应用中的对象。
每个应用都有它自己的结构,类,以及内部的逻辑。但是你无需知道另外一个应用是怎么工作的或者它的什么组件将与它通信。作为一个外部人员,你所需要知道的就是你可以发送什么消息(协议)以及在哪发送它们()
如果一个应用发布了一个对象来作为远程消息的潜在接收器,它就必须还发布一个协议,协议中声明该对象将用来响应那些消息的方法。它无需披露有关该对象的任何其它信息。发送消息的应用不需要知道该对象的类,亦不用在它自己的设计中使用那个类。它只需要那个协议就够了。
协议使匿名对象称为可能。没有了协议,就没办法给一个没有自己类标识的对象声明接口。
注意: 即使一个匿名对象的提供者不返回它的类,但对象自身会在运行时返回它。一个类消息会返回这个匿名对象的类。然而,去发掘这些额外的信息没太大必要。在协议中的信息就足够了。
非继承的共性
如果多于一个类实现了一组方法,这些类通常根据一个抽象类分组,这个抽象类声明了它们公有的那些方法。每个子类可以按照它们自己的方式再实现这些方法,但继承关系以及在抽象类中的公有方法声明持有了子类的基本共性。
然而,有时没办法通过抽象类分组公有方法。在大多方面都不相干的类仍然需要实现一些相似的方法。这种有限的共性可能不适合继承关系。例如,你可能想要在你的应用中添加对创建XM格式对象的支持,并用XML格式初始化变量:
- (NSXMLElement *)XMLRepresentation;
- initFromXMLRepresentation:(NSXMLElement *)xmlString;
这些方法可以重组在协议中,并且实现类的共性通过声明它们都遵循了相同的协议来实现。
对象可以通过这种共性(它们遵循的协议)赋予类型,而不是通过它们的类。例如,一个NSMatrix 实例必须跟代表它的单元的对象通信 。 matrix 可以要求这些对象都是NSCell类型 (基于类的一种类型) 并且依赖于事实上所有从 NSCell 类继承而来的对象都有用于回应 NSMatrix 消息的方法. 另外一种选择是, NSMatrix 对象可以要求代表单元的对象拥有可以响应一组特定消息(基于一种协议类型)的方法。这种情况下, NSMatrix 对象不用在意一个单元对象是属于什么类,只要它实现了那些方法就行。
Formal Protocols
Objective-C 语言提供一种正式声明一组方法(包括属性的声明)的途径,这就是协议。正式的协议获得了这个语言以及运行时系统的良好支持。例如,编译器能够检查基于协议的类型,并且对象可以在运行时自省来报告它们是否遵循一个协议。
声明一个协议
通过一个 @protocol 指令来声明一个正式的协议:
@protocol ProtocolName
method declarations
@end
例如,你可以像这样声明一个XML格式的协议
@protocol MyXMLSupport
- initFromXMLRepresentation:(NSXMLElement *)XMLElement;
- (NSXMLElement *)XMLRepresentation;
@end
不像类名,协议没有全局可见性,它们存在与它们自己的命名空间中。
可选的协议方法
协议方法可以通过 @optional 关键字标识为可选. 与 @optional关键字对应的, 还有个 @required 关键字来正式指定默认行为。你可以使用 @optional 跟 @required 来将你的协议进行适当的分块。如果你没有指定任何关键字,那么默认为 @required.
@protocol MyProtocol
- (void)requiredMethod;
@optional
- (void)anOptionalMethod;
- (void)anotherOptionalMethod;
@required
- (void)anotherRequiredMethod;
@end
注意: 在 Mac OS X v10.5 中,协议不能包含可选声明属性。这个限制在Mac OS X v10.6 以及更高版本中移除。
非正式协议
除了正式协议,你也可以定义一个非正式协议。通过将方法组织在一个类目声明中:
@interface NSObject ( MyXMLSupport )
- initFromXMLRepresentation:(NSXMLElement *)XMLElement;
- (NSXMLElement *)XMLRepresentation;
@end
非正式的协议通常声明为 NSObject 类的类别, 因为这将极大的将方法名与任何继承自NSObject类的类联系起来。因为所有的类都是继承自根类,方法在继承树中的任何地方都没有什么严格限制。(同样可以把非正式协议作为两外一个类的类别,来把它限制在一个特定的继承树分支中,但这真心没什么必要)
在用于一个协议时,一个类别接口没有响应的实现。取而代之的是,实现协议的类在它们自己的接口文件中再次声明这些方法,并在它们的实现文件中把这些方法跟其它方法一起定义。
一个非正式协议改变了类别声明的规则,列出了一组方法,但没有将它们与任何特定类或者实现联系起来。
作为非正式协议,定义在类别中,并不能获得很多语言上的支持。既没有编译时的类型检查,运行时也不能检查一个对象是否遵循了这个协议。要获得这些支持,你必须使用正式协议。非正式协议可能当所有方法都是可选时比较有用,例如 delegate, 但是 (在 Mac OS X v10.5 以及更新的版本中) 最好还是使用带有可选方法的正式协议;
协议对象
就像类对象在运行时代表类,选择器代码代表方法一样,一种特殊的数据类型代表了正式协议—协议类的实例. 跟协议打交道的源码(而不是在一个类型配置中使用它)必须引用相应的协议对象。
在许多时候,协议跟类的定义很像。它们都声明方法,并且在运行时都由对象代表—类是通过类的实例,协议则是协议的实例。向类对象一样,协议对象从源码中找到的声明跟定义自动被创建,并为运行时系统所使用。它们没有在程序源码中被分配并初始化。
源码可以通过使用 @protocol() 指令来使用协议对象—跟声明协议的指令一样, 当然,除了这里多了一对括号。括号中包含了协议名:
Protocol *myXMLSupportProtocol = @protocol(MyXMLSupport);
这是源码召唤(呵呵,或者说使用)协议对象的唯一方法。不像类名,协议名不指定对象—除非在 @protocol()里面。
编译器为每个它遇到的协议声明创建一个协议对象,但仅当该协议同时也是:
被一个类所采用, 或者
在源码中某个地方被引用 (使用 @protocol())
在运行时,不会有协议变量代表那些声明了但没有使用的协议(除非像下面描述的类型检查)
采用一个协议
采用一个协议类似于用某种方法声明一个父类。因为两者都会给这个类分配方法。父类的声明分配它继承的方法。协议分配声明在协议列表中的方法。如果一个类在它的声明中,把协议列在了它的父类名后面的尖括号中,我们就说它采用了一个正式协议,
@interface ClassName : ItsSuperclass < protocol list >
在类别中采用协议也类似:
@interface ClassName ( CategoryName ) < protocol list >
一个类可以采用多个协议;协议列表通过逗号隔开。
@interface Formatter : NSObject < Formatting, Prettifying >
采用了协议的类或者类别必须实现所有协议声明中的要求实现的方法,否则编译器将给出警告。
采用了协议的类或者类别必须导入协议位置头文件。声明在被采用了的协议中的方法不会在类或者类接口中再次声明。
一个类可以仅仅采用协议而不定义别的其它方法。例如,下例中的类就仅仅是声明采用了两个协议,并没声明别的实例变量或者它自己的方法。
@interface Formatter : NSObject < Formatting, Prettifying >
@end
遵循协议
如果一个类采用了正式协议或者继承了一个采用了正式协议的类,那么我们就说它遵循了该协议。而且该类的实例跟它的类遵循了相同的一组协议。
因为类必须实现它所采用的协议中声明的要求实现的所有方法,所以说一个类或者实例遵循了一个协议就等于说它集成了所有声明在协议中的方法。
我们可以通过给一个对象发送
conformsToProtocol: 消息来检查其是否遵循了某个协议.
if ( ! [receiver conformsToProtocol:@protocol(MyXMLSupport)] ) {
// Object does not conform to MyXMLSupport protocol
// If you are expecting receiver to implement methods declared in the
// MyXMLSupport protocol, this is probably an error
}
(要注意还有雷格类方法也叫—
conformsToProtocol:.)
conformsToProtocol: 的测试就像
respondsToSelector: 对单个方法的测试, 除了这里是测试协议是否被很好遵循 (并且可能它声明的所有方法都被实现了) 而不是仅仅看是否某个特定的方法是否被实现。因为它会检查所有协议中的方法,conformsToProtocol: 比
respondsToSelector:更有效。
conformsToProtocol: 测试也跟 isKindOfClass: 测试有点相像, 除了前者是测试是否一个类型是基于协议的而不是基于继承关系的类型。
类型检查
可以扩展对象的类型声明来包含正式协议。这样协议可以提供给编译器另外一种级别的类型检查,会更加抽象,因为它没有跟特定实现绑定。
在类型声明中,协议名列在类型名后面的尖括号里。
- (id <Formatting>)formattingService;
id <MyXMLSupport> anObject;
就像静态赋予类型允许编译器测试基于类继承的类型一样,这个语法也允许基于遵循协议的类型测试。
例如,假如 Formatter 是一个抽象类,声明如下
Formatter *anObject;
将所有继承自这个类的对象归为一个类型并允许编译器对照类型进行类型赋予匹配检查。
相似的,我们声明
id <Formatting> anObject;
将所有遵循 Formatting 协议的对象归为一个类型,忽略它们在类继承关系中的位置。编译器可以确保只有遵循协议的对象被赋予这个类型。
在每个案例中,都是将相似对象归类—或者因为它们共享公共的继承,或者说它们聚集了一组通用方法。
这两种类型可以合并到一个声明中:
Formatter <Formatting> *anObject;
协议不能用于给类对象赋予类型。协议只能给实例赋予静态类型,就像实例可以被静态赋予类型给一个类。 (但是,在运行时,类和实例都可以对conformsToProtocol: 消息进行响应.)
协议中的协议
协议中可以使用其它协议,语法跟类采用一个协议一样。
@protocol ProtocolName < protocol list >
所有尖括号间的协议都被当做 ProtocolName 协议的一部分. 例如, Paging 协议结合了 Formatting 协议
@protocol Paging < Formatting >
任何遵循 Paging 协议的对象也都遵循 Formatting. 类型声明如下
id <Paging> someObject;
conformsToProtocol: 消息如下
if ( [anotherObject conformsToProtocol:@protocol(Paging)] )
...
需要一提的是,只有 Paging 协议检测了 Formatting 的遵循性。
当一个类采用了一个协议,它就必须实现协议声明的所有要求实现的方法。另外,它必须遵循它所采用的协议中集成的其它所有协议。如果一个被集合的协议还集合了别的协议,这个类就必须遵循所有这些协议。类抗议通过下面集中技术来遵循协议:
实现协议声明的要求实现的方法。
继承一个已经采用并实现了该协议的类。
假设, Pager 类采用了 Paging 协议. 要是 Pager 是 NSObject 的子类:
@interface Pager : NSObject < Paging >
就必须实现所有 Paging 的方法,包括声明在集成的 Formatting 协议中的方法。
另一方面,如果Pager 是 Formatter (任意一个采用了 Formatting协议的类) 的子类
@interface Pager : Formatter < Paging >
则必须实现所有声明在Paging 协议中的方法, 但声明在Formatting中的则不用。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 协议所在文件。
英文原文:(
点击打开链接)
Protocols
Protocols declare methods that can be implemented by any class. Protocols are useful in at least three situations:
To declare methods that others are expected to implement
To declare the interface to an object while concealing its class
To capture similarities among classes that are not hierarchically related
Declaring Interfaces for Others to Implement
Class and category interfaces declare methods that are associated with a particular class—mainly methods that the class implements. Informal and formal protocols, on the other hand, declare methods that are independent of any specific class, but which any class, and perhaps many classes, might implement.
A protocol is simply a list of method declarations, unattached to a class definition. For example, these methods that report user actions on the mouse could be gathered into a protocol:
- (void)mouseDown:(NSEvent *)theEvent;
- (void)mouseDragged:(NSEvent *)theEvent;
- (void)mouseUp:(NSEvent *)theEvent;
Any class that wanted to respond to mouse events could adopt the protocol and implement its methods.
Protocols free method declarations from dependency on the class hierarchy, so they can be used in ways that classes and categories cannot. Protocols list methods that are (or may be) implemented somewhere, but the identity of the class that implements them is not of interest. What is of interest is whether or not a particular classconforms to the protocol—whether it has implementations of the methods the protocol declares. Thus objects can be grouped into types not just on the basis of similarities resulting from inheriting from the same class, but also on the basis of their similarity in conforming to the same protocol. Classes in unrelated branches of the inheritance hierarchy might be typed alike because they conform to the same protocol.
Protocols can play a significant role in object-oriented design, especially when a project is divided among many implementors or it incorporates objects developed in other projects. Cocoa software uses protocols heavily to support interprocess communication through Objective-C messages.
However, an Objective-C program doesn’t need to use protocols. Unlike class definitions and message expressions, they’re optional. Some Cocoa frameworks use them; some don’t. It all depends on the task at hand.
Methods for Others to Implement
If you know the class of an object, you can look at its interface declaration (and the interface declarations of the classes it inherits from) to find what messages it responds to. These declarations advertise the messages it can receive. Protocols provide a way for it to also advertise the messages it sends.
Communication works both ways; objects send messages as well as receive them. For example, an object might delegate responsibility for a certain operation to another object, or it may on occasion simply need to ask another object for information. In some cases, an object might be willing to notify other objects of its actions so that they can take whatever collateral measures might be required.
If you develop the class of the sender and the class of the receiver as part of the same project (or if someone else has supplied you with the receiver and its interface file), this communication is easily coordinated. The sender simply imports the interface file of the receiver. The imported file declares the method selectors the sender uses in the messages it sends.
However, if you develop an object that sends messages to objects that aren’t yet defined—objects that you’re leaving for others to implement—you won’t have the receiver’s interface file. You need another way to declare the methods you use in messages but don’t implement. A protocol serves this purpose. It informs the compiler about methods the class uses and also informs other implementors of the methods they need to define to have their objects work with yours.
Suppose, for example, that you develop an object that asks for the assistance of another object by sending ithelpOut: and other messages. You provide an assistant instance variable to record the outlet for these messages and define a companion method to set the instance variable. This method lets other objects register themselves as potential recipients of your object’s messages:
- setAssistant:anObject
{
assistant = anObject;
}
Then, whenever a message is to be sent to the assistant, a check is made to be sure that the receiver implements a method that can respond:
- (BOOL)doWork
{
...
if ( [assistant respondsToSelector:@selector(helpOut:)] ) {
[assistant helpOut:self];
return YES;
}
return NO;
}
Because, at the time you write this code, you can’t know what kind of object might register itself as theassistant, you can only declare a protocol for the helpOut: method; you can’t import the interface file of the class that implements it.
Declaring Interfaces for Anonymous Objects
A protocol can be used to declare the methods of an anonymous object, an object of unknown class. An anonymous object may represent a service or handle a limited set of functions, especially when only one object of its kind is needed. (Objects that play a fundamental role in defining an application’s architecture and objects that you must initialize before using are not good candidates for anonymity.)
Objects are not anonymous to their developers, of course, but they are anonymous when the developer supplies them to someone else. For example, consider the following situations:
Someone who supplies a framework or a suite of objects for others to use can include objects that are not identified by a class name or an interface file. Lacking the name and class interface, users have no way of creating instances of the class. Instead, the supplier must provide a ready-made instance. Typically, a method in another class returns a usable object:
id formatter = [receiver formattingService];
The object returned by the method is an object without a class identity, at least not one the supplier is willing to reveal. For it to be of any use at all, the supplier must be willing to identify at least some of the messages that it can respond to. The messages are identified by associating the object with a list of methods declared in a protocol.
You can send Objective-C messages to remote objects—objects in other applications.
Each application has its own structure, classes, and internal logic. But you don’t need to know how another application works or what its components are to communicate with it. As an outsider, all you need to know is what messages you can send (the protocol) and where to send them (the receiver).
An application that publishes one of its objects as a potential receiver of remote messages must also publish a protocol declaring the methods the object will use to respond to those messages. It doesn’t have to disclose anything else about the object. The sending application doesn’t need to know the class of the object or use the class in its own design. All it needs is the protocol.
Protocols make anonymous objects possible. Without a protocol, there would be no way to declare an interface to an object without identifying its class.
Note: Even though the supplier of an anonymous object doesn’t reveal its class, the object itself reveals it at runtime. A class message returns the anonymous object’s class. However, there’s usually little point in discovering this extra information; the information in the protocol is sufficient.
Nonhierarchical Similarities
If more than one class implements a set of methods, those classes are often grouped under an abstract class that declares the methods they have in common. Each subclass can reimplement the methods in its own way, but the inheritance hierarchy and the common declaration in the abstract class capture the essential similarity between the subclasses.
However, sometimes it’s not possible to group common methods in an abstract class. Classes that are unrelated in most respects might nevertheless need to implement some similar methods. This limited similarity may not justify a hierarchical relationship. For example, you might want to add support for creating XML representations of objects in your application and for initializing objects from an XML representation:
- (NSXMLElement *)XMLRepresentation;
- initFromXMLRepresentation:(NSXMLElement *)xmlString;
These methods could be grouped into a protocol and the similarity between implementing classes accounted for by noting that they all conform to the same protocol.
Objects can be typed by this similarity (the protocols they conform to), rather than by their class. For example, anNSMatrix instance must communicate with the objects that represent its cells. The matrix could require each of these objects to be a kind of NSCell (a type based on class) and rely on the fact that all objects that inherit from the NSCell class have the methods needed to respond to NSMatrix messages. Alternatively, the NSMatrix object could require objects representing cells to have methods that can respond to a particular set of messages (a type based on protocol). In this case, the NSMatrix object wouldn’t care what class a cell object belonged to, just that it implemented the methods.
Formal Protocols
The Objective-C language provides a way to formally declare a list of methods (including declared properties) as a protocol. Formal protocols are supported by the language and the runtime system. For example, the compiler can check for types based on protocols, and objects can introspect at runtime to report whether or not they conform to a protocol.
Declaring a Protocol
You declare formal protocols with the @protocol directive:
@protocol ProtocolName
method declarations
@end
For example, you could declare an XML representation protocol like this:
@protocol MyXMLSupport
- initFromXMLRepresentation:(NSXMLElement *)XMLElement;
- (NSXMLElement *)XMLRepresentation;
@end
Unlike class names, protocol names don’t have global visibility. They live in their own namespace.
Optional Protocol Methods
Protocol methods can be marked as optional using the @optional keyword. Corresponding to the @optionalmodal keyword, there is a @required keyword to formally denote the semantics of the default behavior. You can use @optional and @required to partition your protocol into sections as you see fit. If you do not specify any keyword, the default is @required.
@protocol MyProtocol
- (void)requiredMethod;
@optional
- (void)anOptionalMethod;
- (void)anotherOptionalMethod;
@required
- (void)anotherRequiredMethod;
@end
Note: In Mac OS X v10.5, protocols cannot include optional declared properties. This constraint is removed in Mac OS X v10.6 and later.
Informal Protocols
In addition to formal protocols, you can also define an informal protocol by grouping the methods in a categorydeclaration:
@interface NSObject ( MyXMLSupport )
- initFromXMLRepresentation:(NSXMLElement *)XMLElement;
- (NSXMLElement *)XMLRepresentation;
@end
Informal protocols are typically declared as categories of the NSObject class, because that broadly associates the method names with any class that inherits from NSObject. Because all classes inherit from the root class, the methods aren’t restricted to any part of the inheritance hierarchy. (It is also possible to declare an informal protocol as a category of another class to limit it to a certain branch of the inheritance hierarchy, but there is little reason to do so.)
When used to declare a protocol, a category interface doesn’t have a corresponding implementation. Instead, classes that implement the protocol declare the methods again in their own interface files and define them along with other methods in their implementation files.
An informal protocol bends the rules of category declarations to list a group of methods but not associate them with any particular class or implementation.
Being informal, protocols declared in categories don’t receive much language support. There’s no type checking at compile time nor a check at runtime to see whether an object conforms to the protocol. To get these benefits, you must use a formal protocol. An informal protocol may be useful when all the methods are optional, such as for adelegate, but (in Mac OS X v10.5 and later) it is typically better to use a formal protocol with optional methods.
Protocol Objects
Just as classes are represented at runtime by class objects and methods by selector codes, formal protocols are represented by a special data type—instances of the Protocol class. Source code that deals with a protocol (other than to use it in a type specification) must refer to the corresponding protocol object.
In many ways, protocols are similar to class definitions. They both declare methods, and at runtime they’re both represented by objects—classes by instances of Class and protocols by instances of Protocol. Like class objects, protocol objects are created automatically from the definitions and declarations found in source code and are used by the runtime system. They’re not allocated and initialized in program source code.
Source code can refer to a protocol object using the @protocol() directive—the same directive that declares a protocol, except that here it has a set of trailing parentheses. The parentheses enclose the protocol name:
Protocol *myXMLSupportProtocol = @protocol(MyXMLSupport);
This is the only way that source code can conjure up a protocol object. Unlike a class name, a protocol name doesn’t designate the object—except inside @protocol().
The compiler creates a protocol object for each protocol declaration it encounters, but only if the protocol is also:
Adopted by a class, or
Referred to somewhere in source code (using @protocol())
Protocols that are declared but not used (except for type checking as described below) aren’t represented by protocol objects at runtime.
Adopting a Protocol
Adopting a protocol is similar in some ways to declaring a superclass. Both assign methods to the class. The superclass declaration assigns it inherited methods; the protocol assigns it methods declared in the protocol list. A class is said to adopt a formal protocol if in its declaration it lists the protocol within angle brackets after the superclass name:
@interface ClassName : ItsSuperclass < protocol list >
Categories adopt protocols in much the same way:
@interface ClassName ( CategoryName ) < protocol list >
A class can adopt more than one protocol; names in the protocol list are separated by commas.
@interface Formatter : NSObject < Formatting, Prettifying >
A class or category that adopts a protocol must implement all the required methods the protocol declares, otherwise the compiler issues a warning. The Formatter class above would define all the required methods declared in the two protocols it adopts, in addition to any it might have declared itself.
A class or category that adopts a protocol must import the header file where the protocol is declared. The methods declared in the adopted protocol are not declared elsewhere in the class or category interface.
It’s possible for a class to simply adopt protocols and declare no other methods. For example, the following class declaration adopts the Formatting and Prettifying protocols, but declares no instance variables or methods of its own:
@interface Formatter : NSObject < Formatting, Prettifying >
@end
Conforming to a Protocol
A class is said to conform to a formal protocol if it adopts the protocol or inherits from another class that adopts it. An instance of a class is said to conform to the same set of protocols its class conforms to.
Because a class must implement all the required methods declared in the protocols it adopts, saying that a class or an instance conforms to a protocol is equivalent to saying that it has in its repertoire all the methods the protocol declares.
It’s possible to check whether an object conforms to a protocol by sending it a
conformsToProtocol: message.
if ( ! [receiver conformsToProtocol:@protocol(MyXMLSupport)] ) {
// Object does not conform to MyXMLSupport protocol
// If you are expecting receiver to implement methods declared in the
// MyXMLSupport protocol, this is probably an error
}
(Note that there is also a class method with the same name—
conformsToProtocol:.)
The conformsToProtocol: test is like the
respondsToSelector: test for a single method, except that it tests whether a protocol has been adopted (and presumably all the methods it declares implemented) rather than just whether one particular method has been implemented. Because it checks for all the methods in the protocol,conformsToProtocol: can be more efficient than
respondsToSelector:.
The conformsToProtocol: test is also like the isKindOfClass: test, except that it tests for a type based on a protocol rather than a type based on the inheritance hierarchy.
Type Checking
Type declarations for objects can be extended to include formal protocols. Protocols thus offer the possibility of another level of type checking by the compiler, one that’s more abstract since it’s not tied to particular implementations.
In a type declaration, protocol names are listed between angle brackets after the type name:
- (id <Formatting>)formattingService;
id <MyXMLSupport> anObject;
Just as static typing permits the compiler to test for a type based on the class hierarchy, this syntax permits the compiler to test for a type based on conformance to a protocol.
For example, if Formatter is an abstract class, the declaration
Formatter *anObject;
groups all objects that inherit from Formatter into a type and permits the compiler to check assignments against that type.
Similarly, the declaration
id <Formatting> anObject;
groups all objects that conform to the Formatting protocol into a type, regardless of their positions in the class hierarchy. The compiler can make sure only objects that conform to the protocol are assigned to the type.
In each case, the type groups similar objects—either because they share a common inheritance, or because they converge on a common set of methods.
The two types can be combined in a single declaration:
Formatter <Formatting> *anObject;
Protocols can’t be used to type class objects. Only instances can be statically typed to a protocol, just as only instances can be statically typed to a class. (However, at runtime, both classes and instances respond to aconformsToProtocol: message.)
Protocols Within Protocols
One protocol can incorporate other protocols using the same syntax that classes use to adopt a protocol:
@protocol ProtocolName < protocol list >
All the protocols listed between angle brackets are considered part of the ProtocolName protocol. For example, if the Paging protocol incorporates the Formatting protocol
@protocol Paging < Formatting >
any object that conforms to the Paging protocol also conforms to Formatting. Type declarations such as
id <Paging> someObject;
and conformsToProtocol: messages such as
if ( [anotherObject conformsToProtocol:@protocol(Paging)] )
...
need to mention only the Paging protocol to test for conformance to Formatting as well.
When a class adopts a protocol, it must implement the required methods the protocol declares, as mentioned earlier. In addition, it must conform to any protocols the adopted protocol incorporates. If an incorporated protocol incorporates still other protocols, the class must also conform to them. A class can conform to an incorporated protocol using either of these techniques:
Implementing the methods the protocol declares
Inheriting from a class that adopts the protocol and implements the methods
Suppose, for example, that the Pager class adopts the Paging protocol. If Pager is a subclass of NSObject as shown here:
@interface Pager : NSObject < Paging >
it must implement all the Paging methods, including those declared in the incorporated Formatting protocol. It adopts the Formatting protocol along with Paging.
On the other hand, if Pager is a subclass of Formatter (a class that independently adopts the Formattingprotocol) as shown here:
@interface Pager : Formatter < Paging >
it must implement all the methods declared in the Paging protocol proper, but not those declared in Formatting.Pager inherits conformance to the Formatting protocol from Formatter.
Note that a class can conform to a protocol without formally adopting it, simply by implementing the methods declared in the protocol.
Referring to Other Protocols
When working on complex applications, you occasionally find yourself writing code that looks like this:
#import "B.h"
@protocol A
- foo:(id <B>)anObject;
@end
where protocol B is declared like this:
#import "A.h"
@protocol B
- bar:(id <A>)anObject;
@end
In such a situation, circularity results and neither file will compile correctly. To break this recursive cycle, you must use the @protocol directive to make a forward reference to the needed protocol instead of importing the interface file where the protocol is defined:
@protocol B;
@protocol A
- foo:(id <B>)anObject;
@end
Note that using the @protocol directive in this manner simply informs the compiler that B is a protocol to be defined later. It doesn’t import the interface file where protocol B is defined.