您的位置:首页 > 移动开发 > Objective-C

Objective-C 编程语言官网文档(四)-协议

2012-06-01 18:10 363 查看


声明:本文档仅为个人学习过程中顺手翻译之作,方便开发的同胞借鉴参考。如有觉得译的不好不到位的地方,欢迎指正,将及时做出更正

尽量尊重原文档,因为首次Objective-C,有些地方可能直译了没有注意该语言的专有词,希望指正。如需转载,请注明出处


我的编程环境:

IDE:XCODE4.3.1

OS:MACOSX10.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([assistantrespondsToSelector:@selector(helpOut:)]){

[assistanthelpOut:self];

returnYES;

}

returnNO;

}

因为这次你写的这些代码,你并不知道什么样的对象可能会把它自己注册成
assistant
,你只能为
helpOut:
方法声明一个协议;
不用导入实现它的类的接口文件。


为匿名对象声明接口

协议可以用于声明一个匿名对象的方法,一个未知类的对象。一个匿名对象可能代表是一个服务或者处理有限的一组函数,尤其是当只需要该类型的一个对象时。(对象在顶一个应用的架构时扮演着一个基础角色,而且对象在使用前必须初始化,这对于匿名来说可不好)
对象对它们的开发者来说不是匿名的,当然,当它们被开发者提供给别人的时候,它们就是匿名的了。例如下列情形:

有人要提供一个框架或者一套对象给其它人来使用,可以包含没有类名或者接口文件标识的对象。缺少名字和类接口的情况下,用户是无法创建类的接口实例的。取而代之的是,提供者必须给出一个现成的实例。通常来说,在另外一个类的方法来返回一个有用的对象:

idformatter=[receiverformattingService];

这个通过方法返回的对象没有类标识,至少不是提供者想返回的。它要有所用处,提供者就必须愿意标识一些它能响应的消息。通过将这个对象与声明在一个协议中的一系列方法联系起来以完成消息的标识目的。

你可以发送Objective-C消息给远程对象—在别的应用中的对象。
每个应用都有它自己的结构,类,以及内部的逻辑。但是你无需知道另外一个应用是怎么工作的或者它的什么组件将与它通信。作为一个外部人员,你所需要知道的就是你可以发送什么消息(协议)以及在哪发送它们()

如果一个应用发布了一个对象来作为远程消息的潜在***,它就必须还发布一个协议,协议中声明该对象将用来响应那些消息的方法。它无需披露有关该对象的任何其它信息。发送消息的应用不需要知道该对象的类,亦不用在它自己的设计中使用那个类。它只需要那个协议就够了。

协议使匿名对象称为可能。没有了协议,就没办法给一个没有自己类标识的对象声明接口。

注意:即使一个匿名对象的提供者不返回它的类,但对象自身会在运行时返回它。一个类消息会返回这个匿名对象的类。然而,去发掘这些额外的信息没太大必要。在协议中的信息就足够了。


非继承的共性

如果多于一个类实现了一组方法,这些类通常根据一个抽象类分组,这个抽象类声明了它们公有的那些方法。每个子类可以按照它们自己的方式再实现这些方法,但继承关系以及在抽象类中的公有方法声明持有了子类的基本共性。
然而,有时没办法通过抽象类分组公有方法。在大多方面都不相干的类仍然需要实现一些相似的方法。这种有限的共性可能不适合继承关系。例如,你可能想要在你的应用中添加对创建XM格式对象的支持,并用XML格式初始化变量:

-(NSXMLElement*)XMLRepresentation;

-initFromXMLRepresentation:(NSXMLElement*)xmlString;

这些方法可以重组在协议中,并且实现类的共性通过声明它们都遵循了相同的协议来实现。
对象可以通过这种共性(它们遵循的协议)赋予类型,而不是通过它们的类。例如,一个
NSMatrix
实例必须跟代表它的单元的对象通信。matrix可以要求这些对象都是
NSCell类型
(基于类的一种类型)
并且依赖于事实上所有从
NSCell
类继承而来的对象都有用于回应
NSMatrix
消息的方法.另外一种选择是,
NSMatrix
对象可以要求代表单元的对象拥有可以响应一组特定消息(基于一种协议类型)的方法。这种情况下,
NSMatrix
对象不用在意一个单元对象是属于什么类,只要它实现了那些方法就行。


FormalProtocols

Objective-C语言提供一种正式声明一组方法(包括属性的声明)的途径,这就是协议。正式的协议获得了这个语言以及运行时系统的良好支持。例如,编译器能够检查基于协议的类型,并且对象可以在运行时自省来报告它们是否遵循一个协议。


声明一个协议

通过一个
@protocol
指令来声明一个正式的协议:

@protocolProtocolName

methoddeclarations

@end

例如,你可以像这样声明一个XML格式的协议

@protocolMyXMLSupport

-initFromXMLRepresentation:(NSXMLElement*)XMLElement;

-(NSXMLElement*)XMLRepresentation;

@end

不像类名,协议没有全局可见性,它们存在与它们自己的命名空间中。


可选的协议方法

协议方法可以通过
@optional
关键字标识为可选.与
@optional
关键字对应的,
还有个
@required
关键字来正式指定默认行为。你可以使用
@optional
@required
来将你的协议进行适当的分块。如果你没有指定任何关键字,那么默认为
@required
.

@protocolMyProtocol


-(void)requiredMethod;


@optional

-(void)anOptionalMethod;

-(void)anotherOptionalMethod;


@required

-(void)anotherRequiredMethod;


@end

注意:在MacOSXv10.5中,协议不能包含可选声明属性。这个限制在MacOSXv10.6以及更高版本中移除。


非正式协议

除了正式协议,你也可以定义一个非正式协议。通过将方法组织在一个类目声明中:

@interfaceNSObject(MyXMLSupport)

-initFromXMLRepresentation:(NSXMLElement*)XMLElement;

-(NSXMLElement*)XMLRepresentation;

@end

非正式的协议通常声明为
NSObject
类的类别,因为这将极大的将方法名与任何继承自
NSObject
类的类联系起来。因为所有的类都是继承自根类,方法在继承树中的任何地方都没有什么严格限制。(同样可以把非正式协议作为两外一个类的类别,来把它限制在一个特定的继承树分支中,但这真心没什么必要)
在用于一个协议时,一个类别接口没有响应的实现。取而代之的是,实现协议的类在它们自己的接口文件中再次声明这些方法,并在它们的实现文件中把这些方法跟其它方法一起定义。
一个非正式协议改变了类别声明的规则,列出了一组方法,但没有将它们与任何特定类或者实现联系起来。
作为非正式协议,定义在类别中,并不能获得很多语言上的支持。既没有编译时的类型检查,运行时也不能检查一个对象是否遵循了这个协议。要获得这些支持,你必须使用正式协议。非正式协议可能当所有方法都是可选时比较有用,例如delegate,但是(在MacOSXv10.5以及更新的版本中)最好还是使用带有可选方法的正式协议;


协议对象

就像类对象在运行时代表类,选择器代码代表方法一样,一种特殊的数据类型代表了正式协议—协议类的实例.跟协议打交道的源码(而不是在一个类型配置中使用它)必须引用相应的协议对象。
在许多时候,协议跟类的定义很像。它们都声明方法,并且在运行时都由对象代表—类是通过类的实例,协议则是协议的实例。向类对象一样,协议对象从源码中找到的声明跟定义自动被创建,并为运行时系统所使用。它们没有在程序源码中被分配并初始化。
源码可以通过使用
@protocol()
指令来使用协议对象—跟声明协议的指令一样,当然,除了这里多了一对括号。括号中包含了协议名:

Protocol*myXMLSupportProtocol=@protocol(MyXMLSupport);

这是源码召唤(呵呵,或者说使用)协议对象的唯一方法。不像类名,协议名不指定对象—除非在
@protocol()
里面。
编译器为每个它遇到的协议声明创建一个协议对象,但仅当该协议同时也是:

被一个类所采用,或者

在源码中某个地方被引用(使用
@protocol()
)

在运行时,不会有协议变量代表那些声明了但没有使用的协议(除非像下面描述的类型检查)


采用一个协议

采用一个协议类似于用某种方法声明一个父类。因为两者都会给这个类分配方法。父类的声明分配它继承的方法。协议分配声明在协议列表中的方法。如果一个类在它的声明中,把协议列在了它的父类名后面的尖括号中,我们就说它采用了一个正式协议,

@interfaceClassName:ItsSuperclass<protocollist>

在类别中采用协议也类似:

@interfaceClassName(CategoryName)<protocollist>

一个类可以采用多个协议;协议列表通过逗号隔开。

@interfaceFormatter:NSObject<Formatting,Prettifying>

采用了协议的类或者类别必须实现所有协议声明中的要求实现的方法,否则编译器将给出警告。
采用了协议的类或者类别必须导入协议位置头文件。声明在被采用了的协议中的方法不会在类或者类接口中再次声明。
一个类可以仅仅采用协议而不定义别的其它方法。例如,下例中的类就仅仅是声明采用了两个协议,并没声明别的实例变量或者它自己的方法。

@interfaceFormatter:NSObject<Formatting,Prettifying>

@end


遵循协议

如果一个类采用了正式协议或者继承了一个采用了正式协议的类,那么我们就说它遵循了该协议。而且该类的实例跟它的类遵循了相同的一组协议。
因为类必须实现它所采用的协议中声明的要求实现的所有方法,所以说一个类或者实例遵循了一个协议就等于说它集成了所有声明在协议中的方法。
我们可以通过给一个对象发送
conformsToProtocol:
消息来检查其是否遵循了某个协议.

if(![receiverconformsToProtocol:@protocol(MyXMLSupport)]){

//ObjectdoesnotconformtoMyXMLSupportprotocol

//Ifyouareexpectingreceivertoimplementmethodsdeclaredinthe

//MyXMLSupportprotocol,thisisprobablyanerror

}

(要注意还有雷格类方法也叫—
conformsToProtocol:
.)
conformsToProtocol:
的测试就像
respondsToSelector:
对单个方法的测试,
除了这里是测试协议是否被很好遵循(并且可能它声明的所有方法都被实现了)而不是仅仅看是否某个特定的方法是否被实现。因为它会检查所有协议中的方法,
conformsToProtocol:
respondsToSelector:
更有效。
conformsToProtocol:
测试也跟
isKindOfClass:
测试有点相像,
除了前者是测试是否一个类型是基于协议的而不是基于继承关系的类型。


类型检查

可以扩展对象的类型声明来包含正式协议。这样协议可以提供给编译器另外一种级别的类型检查,会更加抽象,因为它没有跟特定实现绑定。
在类型声明中,协议名列在类型名后面的尖括号里。

-(id<Formatting>)formattingService;

id<MyXMLSupport>anObject;

就像静态赋予类型允许编译器测试基于类继承的类型一样,这个语法也允许基于遵循协议的类型测试。
例如,假如
Formatter
是一个抽象类,声明如下

Formatter*anObject;

将所有继承自这个类的对象归为一个类型并允许编译器对照类型进行类型赋予匹配检查。
相似的,我们声明

id<Formatting>anObject;

将所有遵循Formatting协议的对象归为一个类型,忽略它们在类继承关系中的位置。编译器可以确保只有遵循协议的对象被赋予这个类型。
在每个案例中,都是将相似对象归类—或者因为它们共享公共的继承,或者说它们聚集了一组通用方法。
这两种类型可以合并到一个声明中:

Formatter<Formatting>*anObject;

协议不能用于给类对象赋予类型。协议只能给实例赋予静态类型,就像实例可以被静态赋予类型给一个类。(但是,在运行时,类和实例都可以对
conformsToProtocol:
消息进行响应.)


协议中的协议

协议中可以使用其它协议,语法跟类采用一个协议一样。

@protocolProtocolName<protocollist>

所有尖括号间的协议都被当做ProtocolName协议的一部分.例如,
Paging
协议结合了
Formatting
协议

@protocolPaging<Formatting>

任何遵循
Paging
协议的对象也都遵循
Formatting
.类型声明如下

id<Paging>someObject;

conformsToProtocol:
消息如下

if([anotherObjectconformsToProtocol:@protocol(Paging)])

...

需要一提的是,只有
Paging
协议检测了
Formatting的遵循性。

当一个类采用了一个协议,它就必须实现协议声明的所有要求实现的方法。另外,它必须遵循它所采用的协议中集成的其它所有协议。如果一个被集合的协议还集合了别的协议,这个类就必须遵循所有这些协议。类抗议通过下面集中技术来遵循协议:

实现协议声明的要求实现的方法。

继承一个已经采用并实现了该协议的类。

假设,
Pager
类采用了
Paging
协议.要是
Pager
NSObject
的子类:

@interfacePager:NSObject<Paging>

就必须实现所有
Paging
的方法,包括声明在集成的
Formatting
协议中的方法。
另一方面,如果
Pager
Formatter
(任意一个采用了
Formatting
协议的类)的子类

@interfacePager:Formatter<Paging>

则必须实现所有声明在
Paging
协议中的方法,但声明在
Formatting
中的则不用。
Pager
继承了Formatter对
Formatting
协议的遵循。
要注意类可以不用正式采用一个协议就能遵循该协议,只用简单的实现声明在协议中的方法就可以了。


引用其它协议

当跟复杂的应用打交道时,你偶尔会发现你写的代码看起来是这样的:

#import"B.h"


@protocolA

-foo:(id<B>)anObject;

@end

协议
B
声明如下

#import"A.h"


@protocolB

-bar:(id<A>)anObject;

@end

在这种情况下,形成了死循环,最后结果就是两个文件都无法正确编译。要打破这个死循环,你必须使用
@protocol
指令对需要的协议做一个正向引用,来取代导入定义协议的接口文件。

@protocolB;


@protocolA

-foo:(id<B>)anObject;

@end

注意在使用
@protocol
指令时,在这种写法中只是简单的通知编译器,
B
是一个晚些时候会定义协议。不能导入
B
协议所在文件。

英文原文:(点击打开链接)


Protocols

Protocolsdeclaremethodsthatcanbeimplementedbyanyclass.Protocols
areusefulinatleastthreesituations:

Todeclaremethodsthatothersareexpectedtoimplement

Todeclaretheinterfacetoanobjectwhileconcealingitsclass

Tocapturesimilaritiesamongclassesthatarenothierarchicallyrelated


DeclaringInterfacesforOtherstoImplement

Classandcategoryinterfacesdeclaremethodsthatareassociatedwithaparticularclass—mainly
methodsthattheclassimplements.Informalandformalprotocols,ontheotherhand,declaremethodsthatareindependentof
anyspecificclass,butwhichanyclass,andperhapsmanyclasses,mightimplement.
Aprotocolissimplyalistofmethoddeclarations,unattachedtoaclassdefinition.Forexample,thesemethodsthatreportuseractionsonthemousecouldbegatheredintoaprotocol:

-(void)mouseDown:(NSEvent*)theEvent;

-(void)mouseDragged:(NSEvent*)theEvent;

-(void)mouseUp:(NSEvent*)theEvent;

Anyclassthatwantedtorespondtomouseeventscouldadopttheprotocolandimplementitsmethods.
Protocolsfreemethoddeclarationsfromdependencyontheclasshierarchy,sotheycanbeusedinwaysthatclassesandcategoriescannot.Protocolslistmethodsthatare(ormaybe)implementedsomewhere,but
theidentityoftheclassthatimplementsthemisnotofinterest.Whatisofinterestiswhetherornotaparticularclassconformsto
theprotocol—whetherithasimplementationsofthemethodstheprotocoldeclares.Thusobjectscanbegroupedintotypesnotjustonthebasisofsimilaritiesresultingfrominheritingfromthesameclass,butalsoonthebasisoftheirsimilarityinconforming
tothesameprotocol.Classesinunrelatedbranchesoftheinheritancehierarchymightbetypedalikebecausetheyconformtothesameprotocol.
Protocolscanplayasignificantroleinobject-orienteddesign,especiallywhenaprojectisdividedamongmanyimplementorsoritincorporatesobjectsdevelopedinotherprojects.Cocoasoftwareusesprotocols
heavilytosupportinterprocesscommunicationthroughObjective-Cmessages.
However,anObjective-Cprogramdoesn’tneedtouseprotocols.Unlikeclassdefinitionsandmessageexpressions,they’reoptional.SomeCocoaframeworksusethem;somedon’t.Italldependsonthetaskathand.


MethodsforOtherstoImplement

Ifyouknowtheclassofanobject,youcanlookatitsinterfacedeclaration(andtheinterfacedeclarationsoftheclassesitinheritsfrom)tofindwhatmessagesitrespondsto.Thesedeclarationsadvertise
themessagesitcanreceive.Protocolsprovideawayforittoalsoadvertisethemessagesitsends.
Communicationworksbothways;objectssendmessagesaswellasreceivethem.Forexample,anobjectmightdelegateresponsibilityforacertainoperationtoanotherobject,oritmayonoccasionsimplyneed
toaskanotherobjectforinformation.Insomecases,anobjectmightbewillingtonotifyotherobjectsofitsactionssothattheycantakewhatevercollateralmeasuresmightberequired.
Ifyoudeveloptheclassofthesenderandtheclassofthereceiveraspartofthesameproject(orifsomeoneelsehassuppliedyouwiththereceiveranditsinterfacefile),thiscommunicationiseasilycoordinated.
Thesendersimplyimportstheinterfacefileofthereceiver.Theimportedfiledeclaresthemethodselectorsthesenderusesinthemessagesitsends.
However,ifyoudevelopanobjectthatsendsmessagestoobjectsthataren’tyetdefined—objectsthatyou’releavingforotherstoimplement—youwon’thavethereceiver’sinterfacefile.Youneedanotherway
todeclarethemethodsyouuseinmessagesbutdon’timplement.Aprotocolservesthispurpose.Itinformsthecompileraboutmethodstheclassusesandalsoinformsotherimplementorsofthemethodstheyneedtodefinetohavetheirobjectsworkwithyours.
Suppose,forexample,thatyoudevelopanobjectthatasksfortheassistanceofanotherobjectbysendingit
helpOut:
andother
messages.Youprovidean
assistant
instancevariabletorecordtheoutletforthesemessagesanddefineacompanionmethodtosettheinstancevariable.This
methodletsotherobjectsregisterthemselvesaspotentialrecipientsofyourobject’smessages:

-setAssistant:anObject

{

assistant=anObject;

}

Then,wheneveramessageistobesenttothe
assistant
,acheckismadetobesurethatthereceiverimplementsamethodthat
canrespond:

-(BOOL)doWork

{

...

if([assistantrespondsToSelector:@selector(helpOut:)]){

[assistanthelpOut:self];

returnYES;

}

returnNO;

}

Because,atthetimeyouwritethiscode,youcan’tknowwhatkindofobjectmightregisteritselfasthe
assistant
,youcan
onlydeclareaprotocolforthe
helpOut:
method;youcan’timporttheinterfacefileoftheclassthatimplementsit.


DeclaringInterfacesforAnonymousObjects

Aprotocolcanbeusedtodeclarethemethodsofananonymousobject,
anobjectofunknownclass.Ananonymousobjectmayrepresentaserviceorhandlealimitedsetoffunctions,especiallywhenonlyoneobjectofitskindisneeded.(Objectsthatplayafundamentalroleindefininganapplication’sarchitectureandobjects
thatyoumustinitializebeforeusingarenotgoodcandidatesforanonymity.)
Objectsarenotanonymoustotheirdevelopers,ofcourse,buttheyareanonymouswhenthedevelopersuppliesthemtosomeoneelse.Forexample,considerthefollowingsituations:

Someonewhosuppliesaframeworkorasuiteofobjectsforotherstousecanincludeobjectsthatarenotidentifiedbyaclassnameoraninterfacefile.Lackingthenameandclassinterface,
usershavenowayofcreatinginstancesoftheclass.Instead,thesuppliermustprovideaready-madeinstance.Typically,amethodinanotherclassreturnsausableobject:

idformatter=[receiverformattingService];

Theobjectreturnedbythemethodisanobjectwithoutaclassidentity,atleastnotonethesupplieriswillingtoreveal.Forittobeofanyuseatall,thesuppliermustbewillingto
identifyatleastsomeofthemessagesthatitcanrespondto.Themessagesareidentifiedbyassociatingtheobjectwithalistofmethodsdeclaredinaprotocol.

YoucansendObjective-Cmessagestoremoteobjects—objects
inotherapplications.
Eachapplicationhasitsownstructure,classes,andinternallogic.Butyoudon’tneedtoknowhowanotherapplicationworksorwhatitscomponentsaretocommunicatewithit.Asanoutsider,allyouneed
toknowiswhatmessagesyoucansend(theprotocol)andwheretosendthem(thereceiver).
Anapplicationthatpublishesoneofitsobjectsasapotentialreceiverofremotemessagesmustalsopublishaprotocoldeclaringthemethodstheobjectwillusetorespondtothosemessages.
Itdoesn’thavetodiscloseanythingelseabouttheobject.Thesendingapplicationdoesn’tneedtoknowtheclassoftheobjectorusetheclassinitsowndesign.Allitneedsistheprotocol.

Protocolsmakeanonymousobjectspossible.Withoutaprotocol,therewouldbenowaytodeclareaninterfacetoanobjectwithoutidentifyingitsclass.

Note:Eventhoughthesupplierofananonymousobjectdoesn’trevealitsclass,theobjectitselfrevealsitatruntime.Aclassmessagereturnstheanonymousobject’sclass.However,there’s
usuallylittlepointindiscoveringthisextrainformation;theinformationintheprotocolissufficient.


NonhierarchicalSimilarities

Ifmorethanoneclassimplementsasetofmethods,thoseclassesareoftengroupedunderanabstractclassthat
declaresthemethodstheyhaveincommon.Eachsubclasscanreimplementthemethodsinitsownway,buttheinheritancehierarchyandthecommondeclarationintheabstractclasscapturetheessentialsimilaritybetweenthesubclasses.
However,sometimesit’snotpossibletogroupcommonmethodsinanabstractclass.Classesthatareunrelatedinmostrespectsmightneverthelessneedtoimplementsomesimilarmethods.Thislimitedsimilarity
maynotjustifyahierarchicalrelationship.Forexample,youmightwanttoaddsupportforcreatingXMLrepresentationsofobjectsinyourapplicationandforinitializingobjectsfromanXMLrepresentation:

-(NSXMLElement*)XMLRepresentation;

-initFromXMLRepresentation:(NSXMLElement*)xmlString;

Thesemethodscouldbegroupedintoaprotocolandthesimilaritybetweenimplementingclassesaccountedforbynotingthattheyallconformtothesameprotocol.
Objectscanbetypedbythissimilarity(theprotocolstheyconformto),ratherthanbytheirclass.Forexample,an
NSMatrix
instance
mustcommunicatewiththeobjectsthatrepresentitscells.Thematrixcouldrequireeachoftheseobjectstobeakindof
NSCell
(atypebasedonclass)andrelyonthefact
thatallobjectsthatinheritfromthe
NSCell
classhavethemethodsneededtorespondto
NSMatrix
messages.
Alternatively,the
NSMatrix
objectcouldrequireobjectsrepresentingcellstohavemethodsthatcanrespondtoaparticularsetofmessages(atypebasedonprotocol).In
thiscase,the
NSMatrix
objectwouldn’tcarewhatclassacellobjectbelongedto,justthatitimplementedthemethods.


FormalProtocols

TheObjective-Clanguageprovidesawaytoformallydeclarealistofmethods(includingdeclared
properties)asaprotocol.Formalprotocolsaresupportedbythelanguageandtheruntimesystem.Forexample,thecompilercancheckfortypesbasedonprotocols,andobjectscanintrospectatruntimetoreportwhetherornotthey
conformtoaprotocol.


DeclaringaProtocol

Youdeclareformalprotocolswiththe
@protocol
directive:

@protocolProtocolName

methoddeclarations

@end

Forexample,youcoulddeclareanXMLrepresentationprotocollikethis:

@protocolMyXMLSupport

-initFromXMLRepresentation:(NSXMLElement*)XMLElement;

-(NSXMLElement*)XMLRepresentation;

@end

Unlikeclassnames,protocolnamesdon’thaveglobalvisibility.Theyliveintheirownnamespace.


OptionalProtocolMethods

Protocolmethodscanbemarkedasoptionalusingthe
@optional
keyword.Correspondingtothe
@optional
modal
keyword,thereisa
@required
keywordtoformallydenotethesemanticsofthedefaultbehavior.Youcanuse
@optional
and
@required
to
partitionyourprotocolintosectionsasyouseefit.Ifyoudonotspecifyanykeyword,thedefaultis
@required
.

@protocolMyProtocol


-(void)requiredMethod;


@optional

-(void)anOptionalMethod;

-(void)anotherOptionalMethod;


@required

-(void)anotherRequiredMethod;


@end

Note:InMacOSXv10.5,protocolscannotincludeoptionaldeclaredproperties.ThisconstraintisremovedinMacOSXv10.6andlater.


InformalProtocols

Inadditiontoformalprotocols,youcanalsodefineaninformalprotocolbygroupingthemethodsinacategorydeclaration:

@interfaceNSObject(MyXMLSupport)

-initFromXMLRepresentation:(NSXMLElement*)XMLElement;

-(NSXMLElement*)XMLRepresentation;

@end

Informalprotocolsaretypicallydeclaredascategoriesofthe
NSObject
class,
becausethatbroadlyassociatesthemethodnameswithanyclassthatinheritsfrom
NSObject
.Becauseallclassesinheritfromtherootclass,themethodsaren’trestricted
toanypartoftheinheritancehierarchy.(Itisalsopossibletodeclareaninformalprotocolasacategoryofanotherclasstolimitittoacertainbranchoftheinheritancehierarchy,butthereislittlereasontodoso.)
Whenusedtodeclareaprotocol,acategoryinterfacedoesn’thaveacorrespondingimplementation.Instead,classesthatimplementtheprotocoldeclarethemethodsagainintheirowninterfacefilesanddefine
themalongwithothermethodsintheirimplementationfiles.
Aninformalprotocolbendstherulesofcategorydeclarationstolistagroupofmethodsbutnotassociatethemwithanyparticularclassorimplementation.
Beinginformal,protocolsdeclaredincategoriesdon’treceivemuchlanguagesupport.There’snotypecheckingatcompiletimenoracheckatruntimetoseewhetheranobjectconformstotheprotocol.Toget
thesebenefits,youmustuseaformalprotocol.Aninformalprotocolmaybeusefulwhenallthemethodsareoptional,suchasforadelegate,but(inMacOSXv10.5andlater)itistypicallybettertouseaformalprotocolwithoptionalmethods.


ProtocolObjects

Justasclassesarerepresentedatruntimebyclassobjectsandmethodsbyselectorcodes,formalprotocolsarerepresentedbyaspecialdatatype—instancesofthe
Protocol
class.
Sourcecodethatdealswithaprotocol(otherthantouseitinatypespecification)mustrefertothecorrespondingprotocolobject.
Inmanyways,protocolsaresimilartoclassdefinitions.Theybothdeclaremethods,andatruntimethey’rebothrepresentedbyobjects—classesbyinstancesof
Class
and
protocolsbyinstancesof
Protocol
.Likeclassobjects,protocolobjectsarecreatedautomaticallyfromthedefinitionsanddeclarationsfoundinsourcecodeandareusedby
theruntimesystem.They’renotallocatedandinitializedinprogramsourcecode.
Sourcecodecanrefertoaprotocolobjectusingthe
@protocol()
directive—thesamedirectivethatdeclaresaprotocol,except
thathereithasasetoftrailingparentheses.Theparenthesesenclosetheprotocolname:

Protocol*myXMLSupportProtocol=@protocol(MyXMLSupport);

Thisistheonlywaythatsourcecodecanconjureupaprotocolobject.Unlikeaclassname,aprotocolnamedoesn’tdesignatetheobject—exceptinside
@protocol()
.
Thecompilercreatesaprotocolobjectforeachprotocoldeclarationitencounters,butonlyiftheprotocolisalso:

Adoptedbyaclass,or

Referredtosomewhereinsourcecode(using
@protocol()
)

Protocolsthataredeclaredbutnotused(exceptfortypecheckingasdescribedbelow)aren’trepresentedbyprotocolobjectsatruntime.


AdoptingaProtocol

Adoptingaprotocolissimilarinsomewaystodeclaringasuperclass.Bothassignmethodstotheclass.Thesuperclassdeclarationassignsitinheritedmethods;theprotocolassignsitmethodsdeclaredinthe
protocollist.Aclassissaidtoadoptaformalprotocolifinitsdeclarationitliststheprotocolwithinanglebrackets
afterthesuperclassname:

@interfaceClassName:ItsSuperclass<protocollist>

Categoriesadoptprotocolsinmuchthesameway:

@interfaceClassName(CategoryName)<protocollist>

Aclasscanadoptmorethanoneprotocol;namesintheprotocollistareseparatedbycommas.

@interfaceFormatter:NSObject<Formatting,Prettifying>

Aclassorcategorythatadoptsaprotocolmustimplementalltherequiredmethodstheprotocoldeclares,otherwisethecompilerissuesawarning.TheFormatterclassabovewoulddefinealltherequiredmethods
declaredinthetwoprotocolsitadopts,inadditiontoanyitmighthavedeclareditself.
Aclassorcategorythatadoptsaprotocolmustimporttheheaderfilewheretheprotocolisdeclared.Themethodsdeclaredintheadoptedprotocolarenotdeclaredelsewhereintheclassorcategoryinterface.
It’spossibleforaclasstosimplyadoptprotocolsanddeclarenoothermethods.Forexample,thefollowingclassdeclarationadoptsthe
Formatting
and
Prettifying
protocols,
butdeclaresnoinstancevariablesormethodsofitsown:

@interfaceFormatter:NSObject<Formatting,Prettifying>

@end


ConformingtoaProtocol

Aclassissaidtoconformtoaformalprotocolifitadoptstheprotocolorinheritsfromanotherclassthatadoptsit.Aninstanceofaclassissaidtoconformtothesamesetofprotocols
itsclassconformsto.
Becauseaclassmustimplementalltherequiredmethodsdeclaredintheprotocolsitadopts,sayingthataclassoraninstanceconformstoaprotocolisequivalenttosayingthatithasinitsrepertoireall
themethodstheprotocoldeclares.
It’spossibletocheckwhetheranobjectconformstoaprotocolbysendingita
conformsToProtocol:
message.

if(![receiverconformsToProtocol:@protocol(MyXMLSupport)]){

//ObjectdoesnotconformtoMyXMLSupportprotocol

//Ifyouareexpectingreceivertoimplementmethodsdeclaredinthe

//MyXMLSupportprotocol,thisisprobablyanerror

}

(Notethatthereisalsoaclassmethodwiththesamename—
conformsToProtocol:
.)
The
conformsToProtocol:
testislikethe
respondsToSelector:
test
forasinglemethod,exceptthatittestswhetheraprotocolhasbeenadopted(andpresumablyallthemethodsitdeclaresimplemented)ratherthanjustwhetheroneparticularmethodhasbeenimplemented.Becauseitchecksforallthemethodsintheprotocol,
conformsToProtocol:
can
bemoreefficientthan
respondsToSelector:
.
The
conformsToProtocol:
testisalsolikethe
isKindOfClass:
test,
exceptthatittestsforatypebasedonaprotocolratherthanatypebasedontheinheritancehierarchy.


TypeChecking

Typedeclarationsforobjectscanbeextendedtoincludeformalprotocols.Protocolsthusoffer
thepossibilityofanotherleveloftypecheckingbythecompiler,onethat’smoreabstractsinceit’snottiedtoparticularimplementations.
Inatypedeclaration,protocolnamesarelistedbetweenanglebracketsafterthetypename:

-(id<Formatting>)formattingService;

id<MyXMLSupport>anObject;

Justasstatictypingpermitsthecompilertotestforatypebasedontheclasshierarchy,thissyntaxpermitsthecompilertotestforatypebasedonconformancetoaprotocol.
Forexample,if
Formatter
isanabstractclass,thedeclaration

Formatter*anObject;

groupsallobjectsthatinheritfromFormatterintoatypeandpermitsthecompilertocheckassignmentsagainstthattype.
Similarly,thedeclaration

id<Formatting>anObject;

groupsallobjectsthatconformtotheFormattingprotocolintoatype,regardlessoftheirpositionsintheclasshierarchy.Thecompilercanmakesureonlyobjectsthatconformtotheprotocolareassigned
tothetype.
Ineachcase,thetypegroupssimilarobjects—eitherbecausetheyshareacommoninheritance,orbecausetheyconvergeonacommonsetofmethods.
Thetwotypescanbecombinedinasingledeclaration:

Formatter<Formatting>*anObject;

Protocolscan’tbeusedtotypeclassobjects.Onlyinstancescanbestaticallytypedtoaprotocol,justasonlyinstancescanbestaticallytypedtoaclass.(However,atruntime,bothclassesandinstances
respondtoa
conformsToProtocol:
message.)


ProtocolsWithinProtocols

Oneprotocolcanincorporateotherprotocolsusingthesamesyntaxthatclassesusetoadopta
protocol:

@protocolProtocolName<protocollist>

AlltheprotocolslistedbetweenanglebracketsareconsideredpartoftheProtocolNameprotocol.Forexample,ifthe
Paging
protocol
incorporatesthe
Formatting
protocol

@protocolPaging<Formatting>

anyobjectthatconformstothe
Paging
protocol
alsoconformsto
Formatting
.Typedeclarationssuchas

id<Paging>someObject;

and
conformsToProtocol:
messagessuchas

if([anotherObjectconformsToProtocol:@protocol(Paging)])

...

needtomentiononlythe
Paging
protocoltotestforconformanceto
Formatting
as
well.
Whenaclassadoptsaprotocol,itmustimplementtherequiredmethodstheprotocoldeclares,asmentionedearlier.Inaddition,itmustconformtoanyprotocolstheadoptedprotocolincorporates.Ifanincorporated
protocolincorporatesstillotherprotocols,theclassmustalsoconformtothem.Aclasscanconformtoanincorporatedprotocolusingeitherofthesetechniques:

Implementingthemethodstheprotocoldeclares

Inheritingfromaclassthatadoptstheprotocolandimplementsthemethods

Suppose,forexample,thatthe
Pager
classadoptsthe
Paging
protocol.
If
Pager
isasubclassof
NSObject
asshownhere:

@interfacePager:NSObject<Paging>

itmustimplementallthe
Paging
methods,includingthosedeclaredintheincorporated
Formatting
protocol.
Itadoptsthe
Formatting
protocolalongwith
Paging
.
Ontheotherhand,if
Pager
isasubclassof
Formatter
(a
classthatindependentlyadoptsthe
Formatting
protocol)asshownhere:

@interfacePager:Formatter<Paging>

itmustimplementallthemethodsdeclaredinthe
Paging
protocolproper,butnotthosedeclaredin
Formatting
.
Pager
inherits
conformancetothe
Formatting
protocolfrom
Formatter
.
Notethataclasscanconformtoaprotocolwithoutformallyadoptingit,simplybyimplementingthemethodsdeclaredintheprotocol.


ReferringtoOtherProtocols

Whenworkingoncomplexapplications,youoccasionallyfindyourselfwritingcodethatlookslikethis:

#import"B.h"


@protocolA

-foo:(id<B>)anObject;

@end

whereprotocol
B
isdeclaredlikethis:

#import"A.h"


@protocolB

-bar:(id<A>)anObject;

@end

Insuchasituation,circularityresultsandneitherfilewillcompilecorrectly.Tobreakthisrecursivecycle,youmustusethe
@protocol
directive
tomakeaforwardreferencetotheneededprotocolinsteadofimportingtheinterfacefilewheretheprotocolisdefined:

@protocolB;


@protocolA

-foo:(id<B>)anObject;

@end

Notethatusingthe
@protocol
directiveinthismannersimplyinformsthecompilerthat
B
is
aprotocoltobedefinedlater.Itdoesn’timporttheinterfacefilewhereprotocol
B
isdefined.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: