Objective-C Sucintamente Protocolos

En Objective-C, una protocolo es un grupo de métodos que pueden ser implementados por cualquier clase. Los protocolos son esencialmente los mismos que las interfaces en C #, y ambos tienen objetivos similares. Se pueden usar como un tipo de pseudodatos, lo que es útil para garantizar que un objeto de tipo dinámico pueda responder a un determinado conjunto de mensajes. Y, dado que cualquier clase puede "adoptar" un protocolo, se puede usar para representar una API compartida entre clases completamente no relacionadas.

La documentación oficial analiza tanto un método informal como uno formal para declarar protocolos, pero los protocolos informales son realmente un uso único de categorías y no brindan tantos beneficios como los protocolos formales. Con esto en mente, este capítulo se enfoca únicamente en formal protocolos.


Creando un protocolo

Primero, echemos un vistazo a cómo declarar un protocolo formal. Cree un nuevo archivo en Xcode y seleccione el icono del protocolo Objective-C en Mac OS X> Cocoa:

Icono de Xcode para archivos de protocolo

Como de costumbre, esto le pedirá un nombre. Nuestro protocolo contendrá métodos para calcular las coordenadas de un objeto, así que llamémoslo CoordinateSupport:

Nombrando el protocolo

Hacer clic Siguiente y elija la ubicación predeterminada para el archivo. Esto creará un protocolo vacío que se verá casi exactamente como una interfaz:

// CoordinateSupport.h #import  @protocol CoordinateSupport  @fin

Por supuesto, en lugar de @interfaz directiva, utiliza @protocolo, seguido del nombre del protocolo. los la sintaxis nos permite incorporar otro protocolo en CoordinateSupport. En este caso, estamos diciendo que. CoordinateSupport También incluye todos los métodos declarados en el NSObject protocolo (no debe confundirse con el NSObject clase).

A continuación, vamos a agregar algunos métodos y propiedades al protocolo. Esto funciona de la misma manera que declarar métodos y propiedades en una interfaz:

#importar  @protocol CoordinateSupport  @property double x; @property double y; @property double z; - (NSArray *) arrayFromPosition; - (doble) magnitud; @fin

Adoptar un protocolo

Cualquier clase que adopte este protocolo está garantizada para sintetizar el X, y, y z propiedades e implementar el arrayFromPosition y magnitud metodos Si bien esto no dice cómo se implementarán, le da la oportunidad de definir una API compartida para un conjunto arbitrario de clases.

Por ejemplo, si queremos tanto Enviar y Persona Para poder responder a estas propiedades y métodos, podemos decirles que adopten el protocolo colocándolo entre paréntesis angulares después de la declaración de la superclase. También tenga en cuenta que, al igual que con otra clase, debe importar el archivo de protocolo antes de usarlo:

#importar  #import "CoordinateSupport.h" @interface Persona: NSObject  @property (copia) NSString * nombre; @property (strong) NSMutableSet * amigos; - (void) sayHello; - (vacío) diga adiós; @fin

Ahora, además de las propiedades y métodos definidos en esta interfaz, el Persona clase está garantizada para responder a la API definida por CoordinateSupport. Xcode te avisará que el Persona La implementación es incompleta hasta que se sintetiza. X, y, y z, e implementar arrayFromPosition y magnitud:

Advertencia de implementación incompleta para Persona

Del mismo modo, una categoría puede adoptar un protocolo agregándolo después de la categoría. Por ejemplo, para decirle al Persona clase para adoptar el CoordinateSupport protocolo en el Relaciones Categoría, utilizarías la siguiente línea:

Persona Interfaz (Relaciones) 

Y, si su clase necesita adoptar más de un protocolo, puede separarlos con comas:

Persona de la interfaz: NSObject 

Ventajas de los Protocolos

Sin protocolos, tendríamos dos opciones para asegurar que ambos Enviar y Persona implementado esta API compartida:

  1. Volver a declarar exactamente las mismas propiedades y métodos en ambas interfaces.
  2. Defina la API en una superclase abstracta y defina Enviar y Persona como subclases.

Ninguna de estas opciones es particularmente atractiva: la primera es redundante y propensa a errores humanos, y la segunda es muy limitante, especialmente si ya heredan de diferentes clases primarias. Debe quedar claro que los protocolos son mucho más flexibles y reutilizables, ya que evitan que la API dependa de cualquier clase en particular..

El hecho de que alguna la clase puede adoptar fácilmente un protocolo que hace posible definir relaciones horizontales sobre una jerarquía de clases existente:

Vinculación de clases no relacionadas mediante un protocolo

Debido a la naturaleza flexible de los protocolos, los distintos marcos de iOS hacen un buen uso de ellos. Por ejemplo, los controles de la interfaz de usuario a menudo se configuran utilizando el patrón de diseño de delegación, en el que un objeto delegado es responsable de reaccionar ante las acciones del usuario. En lugar de encapsular las responsabilidades de un delegado en una clase abstracta y obligar a los delegados a subclasificarlo, iOS define la API necesaria para el delegado en un protocolo. De esta manera, es increíblemente fácil para alguna objeto para actuar como el objeto delegado. Exploraremos esto con mucho más detalle en la segunda mitad de esta serie., iOS sucintamente.


Protocolos como pseudo tipos

Los protocolos se pueden utilizar como tipos de datos psuedo. En lugar de asegurarse de que una variable sea una instancia de una clase, el uso de un protocolo como herramienta de comprobación de tipos garantiza que la variable siempre se ajuste a una API arbitraria. Por ejemplo, los siguientes persona La variable está garantizada para implementar la API CoordinateSupport..

Persona  * persona = [[Asignación de la persona] init];

Sin embargo, imponer la adopción del protocolo suele ser más útil cuando se usa con el carné de identidad tipo de datos. Esto le permite asumir ciertos métodos y propiedades sin tener en cuenta la clase del objeto..

Y, por supuesto, la misma sintaxis se puede utilizar con un parámetro de método. El siguiente fragmento agrega un nuevo getDistanceFromObject: método a la API cuyo parámetro se requiere para cumplir con CoordinateSupport protocolo:

// CoordinateSupport.h #import  @protocol CoordinateSupport  @property double x; @property double y; @property double z; - (NSArray *) arrayFromPosition; - (doble) magnitud; - (double) getDistanceFromObject: (id )el objeto; @fin

Tenga en cuenta que es completamente posible usar un protocolo en el mismo archivo que se define.

Comprobación dinámica de conformidad

Además de la verificación de tipos estática que se analizó en la última sección, también puede usar la conformsToProtocol: método definido por el NSObject protocolo para verificar dinámicamente si un objeto se ajusta a un protocolo o no. Esto es útil para evitar errores cuando se trabaja con objetos dinámicos (objetos escritos como carné de identidad).

El siguiente ejemplo asume la Persona la clase adopta el CoordinateSupport protocolo, mientras que el Enviar la clase no lo hace Utiliza un objeto de tipo dinámico llamado misterioobjeto para almacenar una instancia de Persona,y luego usa conformsToProtocol: para comprobar si tiene soporte coordinado. Si lo hace, es seguro usar el X, y, y z propiedades, así como los otros métodos declarados en el CoordinateSupport protocolo:

// main.m #import  #importar "Person.h" #importar "Ship.h" int main (int argc, const char * argv []) @autoreleasepool id mysteryObject = [[alloc As]]]; [mysteryObject setX: 10.0]; [mysteryObject setY: 0.0]; [mysteryObject setZ: 7.5]; // Descomente la siguiente línea para ver la parte "else" de condicional. // mysteryObject = [[Ship alloc] init]; if ([mysteryObject conformsToProtocol: @protocol (CoordinateSupport)]) NSLog (@ "Ok para asumir el soporte de coordenadas."); NSLog (@ "El objeto se encuentra en (% 0.2f,% 0.2f,% 0.2f)", [mysteryObject x], [mysteryObject y], [mysteryObject z]);  else NSLog (@ "Error: No es seguro asumir el soporte de coordenadas."); NSLog (@ "No tengo idea de dónde está ese objeto ...");  devolver 0; 

Si descomprime la línea que reasigna el misterioobjeto a un Enviar ejemplo, el conformsToProtocol: el método regresará NO, y no podrá utilizar de forma segura la API definida por CoordinateSupport. Si no está seguro del tipo de objeto que tendrá una variable, este tipo de comprobación dinámica de protocolo es importante para evitar que su programa se bloquee cuando intenta llamar a un método que no existe.

Note también el nuevo @protocolo() directiva. Esto funciona mucho como @selector(), excepto en lugar de un nombre de método, toma un nombre de protocolo. Devuelve un Protocolo objeto, que puede ser pasado a conformsToProtocol:, Entre otros métodos incorporados. El archivo de cabecera del protocolo hace no necesita ser importado para @protocolo() trabajar.


Protocolos de declaración directa

Si terminas trabajando con muchos protocolos, eventualmente te encontrarás con una situación en la que dos protocolos dependen uno del otro. Esta relación circular plantea un problema para el compilador, ya que no puede importar con éxito ninguno de ellos sin el otro. Por ejemplo, digamos que estábamos tratando de abstraer algunas funciones de GPS en un GPSSupport protocolo, pero queremos poder convertir entre las coordenadas "normales" de nuestros existentes CoordinateSupport y las coordenadas utilizadas por GPSSupport. los GPSSupport El protocolo es bastante simple:

#importar  #import "CoordinateSupport.h" @protocol GPSSupport  - (void) copyCoordinatesFromObject: (id )el objeto; @fin

Esto no plantea ningún problema, es decir, hasta que necesitamos hacer referencia a la GPSSupport protocolo desde CoordinateSupport.h:

#importar  #import "GPSSupport.h" @protocol CoordinateSupport  @property double x; @property double y; @property double z; - (NSArray *) arrayFromPosition; - (doble) magnitud; - (double) getDistanceFromObject: (id )el objeto; - (void) copyGPSCoordinatesFromObject: (id )el objeto; @fin

Ahora el CoordinateSupport.h archivo requiere el GPSSupport.h Archivo para compilar correctamente, y viceversa. Es un problema de tipo gallina o huevo, y al compilador no le gustará mucho:

Error del compilador causado por referencias de protocolo circular.

Resolver la relación recursiva es simple. Todo lo que necesita hacer es declarar hacia adelante uno de los protocolos en lugar de intentar importarlo directamente:

#importar  @protocol CoordinateSupport; @protocolo GPSSupport  - (void) copyCoordinatesFromObject: (id )el objeto; @fin

Todos @protocol CoordinateSupport; dice es que CoordinateSupport es de hecho un protocolo y el compilador puede asumir que existe sin importarlo. Tenga en cuenta el punto y coma al final de la declaración. Esto podría hacerse en cualquiera de los dos protocolos; El punto es quitar la referencia circular. Al compilador no le importa cómo lo hagas..


Resumen

Los protocolos son una característica increíblemente poderosa de Objective-C. Le permiten capturar relaciones entre clases arbitrarias cuando no es posible conectarlas con una clase padre común. Utilizaremos varios protocolos incorporados en iOS sucintamente, Como muchas de las funciones principales de una aplicación de iPhone o iPad se definen como protocolos.

El siguiente capítulo presenta excepciones y errores, dos herramientas muy importantes para manejar los problemas que inevitablemente surgen al escribir programas de Objective-C.

Esta lección representa un capítulo de Objective-C Sucintamente, un libro electrónico gratuito del equipo de Syncfusion.