• Català
  • Castellano
  • English


Protocols - Declarant Interficies per que Altres Implementin


Les classes i les interfícies de categories declaren mètodes que estan associades a una classe en particular -- principalment, mètodes que la classe implementa. Els protocols formals i informals, a l'altra banda, declara mètodes no associades a una classe, però que qualsevol classe, i potser moltes classe, poden implementar-lo.

Un protocol és simplement una llista de declaracions de mètodes, no units a la definició de la classe. Per exemple, aquests mètodes que informen de accions d'usuari amb el ratolí poden ser recollits dins d'un protocol:

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

Qualsevol classe que vulgui respondre a events del ratolí ha d'adoptar el protocol i implementar els seus mètodes.

Els protocols alliberen les declaracions dels mètodes de la dependència de la jerarquia de la classe, així poden ser utilitzats en coses que les classes i categories no poden. Els protocols llisten mètodes que són (o poden ser) implementats quan sigui, però la identitat de la classe que els implementa ens és igual. El que interesa és quan una classe en particular s'ajusta al protocol -- quan té implementacions dels mètodes que el protocol declara. Aquests objectes poden ser agrupats dins de tipus que no cal que es basin en semblances degut al fet que heredin de la mateixa classe, però també en base a la seva similitut a l'ajustar-se amb el mateix protocol. Les classes en jerarquies sense relació d'herència poden ser tractades igualment perqué s'ajusten amb el mateix protocol.

Els protocols poden jugar un rol important en el dissent orientat a objectes, especialment quan un projecte està dividit en un munt d'implementacions o aquest incorpora objectes desenvolupats en altres projectes. El programari Cocoa els utilitza molt per suportar la comunicació entre processos a través de missatges Objective-C.

Altrament, un programa Objective-C no necessita l'ús de protocols. Excepte la definició de la classe i les expressions de missatge, l'altre és opcional. Alguns marcs de treball de Cocoa els utilitzen; algun no. Tot depen del tipus de tasca.

Quan Utilitzo Protocols

Els protocols són normalment utilitzats en almenys tres situacions:

  • Per declarar mètodes que altres esperen que implementis.
  • Per declarar la interfície d'un objecte mentre amagen la seva classe.
  • Per capturar semblances per sobre de classes que no tenen relació de jerarquia.

 

Les seccions següents parlen d'aquestes situacions i els rols que els protocols poden jugar.

Mètodes per que Implementin Altres

Si coneixen la classe d'un objecte, pots mirar la seva declaració de la interficie (i les declaracions de les interficies de les classes que ha heredat) per trobar a quins missatges respon. Aquestes declaracions adverteixen els missatges que pots rebre. Els protocols proveeixen una via per que també anuncie els missatges que envia.

La comunicació treballa en ambdues vies; els objectes envien missatges així com els reben. Per exemple, un objecte pot delegar la responsabilitat per a certes operacions a un altre objecte, o pot, en ocasions, simplement necessita preguntar a un altre objecte per una informació. En alguns casos, un objecte pot voler notificar a altres objectes de les seves accions per que puguin prendre mesures colaterals que puguin ser requerides.

Si desenvolupes la classe de l'enviador i la classe del receptor com a part del mateix projecte o si algú en canvi t'ha suministrat el receptor i el fitxer de la seva interficie), aquesta comunicació és coordinada més facilment. El que envia simplement importa el fitxer de la interficie del receptor. El fitxer importat declara els mètodes que el que envia utilitza en els missatges que envia.

Tanmateix, si desenvolupes un objecte que envia missatges a objectes que no has definit -- objectes que tu deixes perqué altres els implementin -- no tens el fitxer de la interfície del receptor. Necessites una altra manera per declarar els mètodes que utilitzes en els missatges però sense implementar-los. Un protocol serveix per aquest propòsit. Informa al compilador sobre els mètodes que la classe utilitza i també informa d'altres implementadors dels mètodes que necessiten definir perque els seus objectes treballin amb els teus.

Suposa, per exemple, que desevolupes un objecte que pregunta per la sistencia d'un altre objecte enviant-li un helpOut: i altres missatges. Tu poveiran una variable assistant per recordar la connexió per aquests missatges i defineixes un mètode de acompanya l'activació de la variable. Aquest mètode permet a altres objectes registrar-se com a potencias recipients pels teus missatges d'objecte:

- setAssistant:anObject
{
assistant = anObject;
}

Llabors, si un missatge s'envia al assistnat, es fa una comprobació per assegurar-se que el receptor implementa un mètode que pot respondre:

- (BOOL)doWork
{
...
if ( [assistant respondsTo:@selector(helpOut:)] ) {
[assistant helpOut:self];
return YES;
}
return NO;
}

Des de, l'hora que escrius aquest codi, no pots saber quina varietat d'objectes poden registrar-se com assitant, només pots declarar un protocol pel mètode helpOut:; no pots importar el fitxer d'interfície de la classe que l'implementa.

Declarant Interfícies per Objectes Anònims

Un protocol pot utilitzar-se per declarar els mètodes perun objecte anònom, un objecte amb classe desconeguda. Un objecte anònim pot representar un servei o manipulador s'un conjunt de funcions limitat, especialment on només un objecte d'aquest pot necessitar-se. (Els objectes que juguen un rol fonamental al definir una arquitectura d'aplicació i els objectes que pots inicialitzar abans d'utilitzar-los no són bons candidats per ser anònims).

Els objectes no són anònims per als seus desenvolupadors, per descomptat, però s'ón anònims quan el desenvolupador els deixa a algú altre. Per exemple, considera les següents situacions:

  • Algú qui deixa un marc de treball o un conjutn d'objectes per que altres els utilitzin i els puguin incloure, que no estan identificats per un nom de classe o un fitxer d'interficie. La carència de nom i d'interfície de classe, els usuaris no tenen forma de crear instàncies per la classe. En lloc d'això, el que ho deixa ha de proporcionar una instància realment feta. Normalment, un mètode en una altra classe que retorna un objecte utilitzable:
    id formatter = [receiver formattingService];

    L'objecte retornat pel mètode és un objecte sense una identitat de classe, al menys cap el distribuidor desitgi revelar. Per que sigui utilitzable per tots, el distribuidor ha de voler identificar al menys algun dels missatges que aquest pot respondre. Aquest és correcte per associar l'objecte amb una llista de mètodes declarats en un protocol.

  • Pots enviar missatges Objective-C a objectes remots -- objectes en altres aplicacions. ("Missatges Remots", tracta aquesta posibilitat amb més detall.)

    Cada aplicació té la seva pròpia estructura, classes, i lògica interna. Però no necessites coneixer com altres aplicacions treballen o quins d'aquests components es comuniquem amb ell. Com un foraster, tot el que necessitis coneixer és quins missatges pots enviar (al protocol) i on enviar-los (el receptor).

    Una aplicació que publica n dels sus objectes com un potencial receptor dels missatges remots ha depublicar un protocol declarant els mètodes que l'objecte haurà d'utilitzar per respondre a aquests missatges. Això no ha de divulgar-se cap cosa sinó l'objecte. La aplicació que envia no necessita coneixer la classe de l'objecte o l'ús de la classe en el seu propi disseny. Tot el que necessita és el protocol.

 

Els protocols fan possible els objectes anònims. Dins un protoocl, hauria de ser una formar per declarar una interficie a un objecte sense indentificar la seva classe.

Nota: Encara que pensis que el distribuidor d'un objecte anònim no revela la seva classe, el propi objecte es revela en temps d'execució. Un missatge de classe retorna una classe d'objecte anònim. Tanmateix, normalment hi han petits punts per descobrir aquesta informació extra; la informació en el prototcol és suficient.

Similituts amb la No-Jerarquia

Si més d'una classe implementa un joc de mètodes, aquestes classes s'ofereixen agrupades sta una classe abstracta que declara els mètodes que ells tenen en comú. Cada subclasse pot reimplementar els mètodes de la seva pròpia forma, però la jerarquia heredada i la declaració comuna en la classe abstracta captura les similituts esencials de les subclasses.

Tanmateix, alguns cops no és possible agrupar mètodes comuns en un classe abstracta. Les classe que no estan relacionades poden no obstant necessitar implementar alguns mètodes similars. Aquesta similitud limitada no pot justificar una relació de jerarquia. Per exemple, molt tipus diferents de classes poden implementar mètodes per facilitar el contatge de referència (aqust és només un exemple, des del Marc de Treball de Fundació també implementa el contatge de referències per tu):

- setRefCount:(int)count;
- (int)refCount;
- incrementCount;
- decrementCount;

Aquests mètodes han d'agrupar-se dins un protocol i semblant entre les classes informades per un notificació que tots ells s'ajusten el mateix protocol.

Els objectes poden ser tipats per aquest de forma semblant (els protocols al que s'ajusten), encara que per les seves classes. Per exemple, un NSMatrix ha de comunicar amb els objectes que representen les seves cel·les. El NSMatrix requereix cada un d'aquels cobjectes per ser una variant de NSCell (un tipus basat en un classe) i confiar en el fet que tots els objectes que hereden des de la classe NSCell tenen els mètodes necesitats per repondre als missatges NSMatrix. Altrament, el NSMatrix hauria de requerir els objectes que representen cel·les que tinguin mètodes que puguin respondre a un particular joc de missatges (un tipus bassat en un protocol). En aquest cas, el NSMatrix no podria preocupar-se quina classe d'objecte cell hi pertany, només que tingui implementats els mètodes.

Protocols Informals

La forma més simple de declarar un protocol és agrupar els mètodes en una declararació de categoria:

@interface NSObject ( RefCounting )
- (int)refCount;
- incrementCount;
- decrementCount;
@end

Els protocols informals són normalment declarats com a categories de l'objecte NSObject, com que els noms de mètodes són ampliament associats amb qualsevol classe que hereda des de NSObject. Perquè totes les classes heredades des de la classe arrel, els mètodes no estan restringits per cap part de la jerarquia. (També podria ser possible declarar un protocol informal com un categoria d'una altra classe per limitar-lo a certes branques de la jerarquia heredada, però hi han poques raons per fer-ho així).

Quan s'utilitza per declarar un protocol, una categoria no té un implementació corresponent. El lloc d'aixó, les classes que implementen el protocol que declara els mètodes altra cop en el fitxer de la seva pròpia interfície i els defineix enllà amb altres mètodes en els fitxers de les seves implementació.

Un protocol informal fa una excepció de les declaracions de categories per llistar un grup de mètodes però no els associa amb cap classe o implementació particular.

Essent infomals, els protocols declarats en categories que no reven molt suport de llenguatge. No hi han comprovació de tipus en temps de compilació ni una comprovació en temps d'execució per veure quan un objecte s'ajusta a un protocol. Per obtindre aquests beneficies, has d'utilitzar protocols formals. Un protocol informal és bo a vegades quan implementen tots els mètodes que són opcionals, així com per una delegació.

Protocols Formal

El llenguatge Objective-C proporciona una forma per declarar formalment una llista de mètodes com un protocol. Els protocols formals estan suportats pel llenguatge i el sistema d'execució. Per exemple, el compilador pot comprovar els tipus basats en protocols, i els objectes poden inspeccionar a l'hora de l'execució per informar si conformen o no un protocol.

Els protocols formals es declaren amb la directiva @protocol:

@protocol NomProtocol
declaracions de mètodes
@end

 

Per exemple, una protocol de contatge podria declarar-se així:

@protocol ReferenceCounting
- (int)refCount;
- incrementCount;
- decrementCount;
@end

 

A diferència de noms de classes, els noms dels protocols no tenen visibilitat global. Resideixen en el seu propi espai de noms.

Una classe es diu que adopta un protocol formal si aquest accepta d'implementar els mètodes que declara el protocol. Les declaracions de les classes llisten els noms dels protocols adoptats dins unes claus (< >) després del nom de la superclasse:

@interface NomDeLaClasse : LaSevaSuperclasse < llista de protocols >

 

Els categories adopten protocols de la mateixa forma:

@interface NomDeLaClasse ( NomDeLaCategoria ) < llista de protocols >

 

Els noms en la llista de protocols estan separats per comes.

Una classe o categoria que adopta un protocol ha d'importar el fitxer de capçalera on el protocol està declarat. Els mètodes declarats en el protocol adoptat no estan declarats enlloc més que en la interfície de la classe o la categoria.

És posible que una classe simplement adopti protocols i no declari altres mètodes. Per exemple, la següent declaració de classe adopten els protocols Formatting i Prettifying, però no declara variables d'instància ni mètodes pròpis:

@interface Formatter : NSObject < Formatting, Prettying >
@end

 

Una classe o categoria que adopta un protocol està obligat a implementar totes els mètodes que els protocol declara. Sinó, el compilador genera advertències. La classe Formatter de sobre hauria de definir tots els mètodes declarats en els dos protocols que adopta, a més de qualsevol que podria haver declara ell mateix.

Adoptar un protocol és similar d'algunes formes per declarar una superclasse. Amdos assignen
mètodes a la classe. La declaració de la superclasse assigna els seus mètodes heredats; el protocol assigna els mètodes declarats en la llista de protocols.

Objectes de Protocol

Així com les classes es representen en temps d'execució per objectes i mètodes de classes per codis de selector, els protocols formals estan representats per un tipus de dades especials -- instàncies de la classe Protocol. El codi font que tracta amb un protocol (altres que l'utilitzen en una especificació de tipus) ha de referir-se als objecte Protocols.

De moltes formes, els protocols són similars a les definicions de les classes. Ambdos declaren mètodes, i en temps d'execució ambdos estan representats per objectes -- classe per objectes de classe i protocols per objectes de Protocol. Com els objectes de classe, els objectes de Protocol són creats automàticament a partir de les definicions i les declaracions trobades en el codi font i s'utiltizen en el sistema d'execució. No s'assignen ni s'inicialitzen un programa de codi font.

El codi font pot referir-se a un objecte de Protocol utilitzant la directiva @protocol() -- la mateixa directiva que declara un protocol, excepte que aquí té un un conjunt de parèntesis. Els parèntesis encerclen el nom del protocol:

Protocol *counter = @protocol (ReferenceCounting);

 

És la única forma que el codi font pugui fer apareixer un objecte Protocol. A diferència d'un nom de classe, un nom de protocol no designa l'objecte -- execpte el de dins de @protocol().

El compilador crea un objecte Protocol per a cada declaració de protocol que troba, però només si el protocol també ho és:

  • Adoptat per una classe, o
  • Referenciat en algun lloc del codi font (utilitzant @protocol()).

 

Els protocols que estan declarats però no utilitzats (excepte per comprovació de tipus com s'explica més avall) no estan representats per objectes Protocol a l'hora de l'execució.

Ajustar-se a un Protocool

Una classe és diu que s'ajusta a un protocol formal si aquest (o la seva superclasse) implementa els mètodes declarats en el protocol. Una instància d'una classe és diu que s'ajusta al mateix conjunt de protocols que la seva classe s'hi ajusta.

Així com una classe ha d'implementar tots els mètodes declarats en els protocols que aquest adopta, i aquests mètoes s'hereden per les seves subclasses, es diu que una classe o una instància s'ajusta a un protocol és equivalent a dir que té en el seu repertori tots els mètodes que declara el protocol.

És posible comprovar si un objecte s'ajusta a un protocol enviant-li un missatge conformsToProtocol:.

if ( [receptor conformsToProtocol:@protocol(ReferenceCounting)])
[receptor incrementCount];

 

La comprovació conformsToProtocol: és molt més que la comprovació de respondsTo:, excepte que comprovi si un protocol ha estat adoptat (i presumptament tots els mètodes que declara implementats) en vés de si un mètode particular està implemetnat. Perqué comprova per una llista completa de mètodes, conformsToProtocol: pot ser més eficient que respondsTo:.

La comprovació conformsToProtocol: també és molt millor que la comprovació isKinkOfClass:, excepte que aquest test sigui per un tipus basat en un protocol en vés d'un tipus basat en una jerarquia d'herència.

Compront Tipus

Les declaracions de tipus pels objectes poden extendre's per incloure els protocols formals. Els protocols ofereixen la possibilitat per altres nivells de comprovació de tipus pel compilador, aquest és més abstracte ja que no està lligat a una implemetació en particular.

En una declaració de tipus, els noms dels protocols estan llistats entre claus després del nom de tipus:

- (id )formattingService;
id anObject;

 

Així com la declaració estàtica pemet que el compilador comprovi el tipus basat en la jerarquia de classes, aquesta sintàxi permet al compilador comprovar un tipus basat i ajustat a un protocol.

Per exemple, si Formatter és un classe abstracta, la seva declaració

Formatter *unObjecte;

agrupa tots els objectes que hereda de Formatter dins un tipus i permet al compilador comprovar assignacions un altre cop a part del tipus.

 

De forma semblant, aquesta declaració,

id  unObjecte;

agrupa tots els objectes que s'ajusten al protocol Formatting dins un tipus, sense tenir en compte les seves posicions en la jerarquia de la clase. El compilado s'assegura que només els objectes que s'ajusten al protocol estan assignats a el tipus.

 

En cada cas, el tipus agrupa objectes similars -- qualsevol perqué ells comparteixen una herència comuna, o perqué convergeixen en un congunt comú de mètodes.

Els dos tipus poden combinarse en una única declaració:

Formatter  *unObjecte;

 

Els protocols no poden utilitzar-se per declarar objectes de classe. Només les instàncies poden declarar-se estàticament a un protocol, així com només les instàncies poden poden ser declarats estàticament a una classe. (Tanmateix, en temps d'execució, ambdues classes i instàncies respondran a el missatge conformsToProtocol:).

Protocols Dins Protocols

Un protocol pot incorporar altres protocols utilitzant la mateixa sintaxi que utilitzen les classes per adoptar un protocol:

@protocol NomDelProtocol < llista de protocols >

 

Tots els protocols llistats entre les claus (< >) es consideren part del protocol NomDelProtocol. Per exemple, si el protocol Paging incorpora el protocol Formatting,

@protocol Paging < Formatting >

qualsevol objecte que s'ajusti a el protocol Pagins també s'ajusta a Formatting. Les declaracions de tipus

id  algunObjecte;

i els missatges conformsToProtocol:

if ( [unAltreObjecte conformsToProtocol:@protocol(Paging)] )
...

necessiten mencionar només el protocol Paging per comprovar que s'ajusten també a Formatting.

 

Quan una classe adopta un protocol, ha d'implementar els mètodes que declara el protocol, com s'ha dit abans. A més, ha d'ajustar-se a qualsevol protocol que el protocol adoptat incorpora. Si un protocol incorporat incorpora fins i tot un altre protocol, la classe també ha d'ajustar-se a ells. Una classe pot ajustar-se a un protocol incorporat per un altre:

  • Implementant els mètodes que declara el protocol, o
  • Heredant d'una classe que adopta el protocol i implementar els mètodes

 

Suposa, per exemple, que la classe Pager adopta el protocol Paging. Si Pager és una subclasse de NSObject,

@interface Pager : NSObject < Paging >

ha d'implementar tots els mètodes de Paging, incluint-hi els declarats en el protocol incorporat Formatting. Aquest adopta el protocol Formatting a través de Paging.

 

Per altra banda, si Pager és una subclasse de Formatter (una classe que independentiment adopta el protocol Formatting),

@interface Pager : Formater < Paging >

ha d'implementar tots els mètodes declarats en el protocol Paging, però no cal declarals els de Formatting. Paper hereda l'ajustament amb el protocol Formatting a través de Formatter.

 

Fixeu-vos que una classe pot ajustar-se a un protocol sense formalment adoptar-lo simplement implementant els mètodes declarats en el protocol.

Referenciant a Altres Protocols

Quan es treball sobre aplicacions complexes, ocasionalment busques escriure codi que semblant a aquest:

#import "B.h"

@protocol A
- foo:(id <B>)unObjecte;
@end

on el protocol B és declarat així:

#import "A.h"

@protocol B
- bar:(id <A>)unObjecte;
@end

 

En aquesta situació, dona com a resultat un cercle viciós i cap fitxer compilarà correctament. Per trencar aquest cicle recursiu, has d'utilitzar la directiva @protocol per fer un referència al protocol necessitat per importar el fitxer de la interfície on està definit el protocol. El següent codi il·lusta com haries de fer-ho:

@protocol B;

@protocol A
- foo:(id <B>)unObjecte;
@end

 

Fixeu-vos que utilitzant la directiva @protocol d'aquesta manera simplement informem al compilador que "B" és un protocol que es definirà més tard. No importa el fitxer d'interfície on està definit el protocol B.