En Objective-C, tenemos dos maneras utilizadas para la recepción y el envío de mensajes entre las clases: las notificaciones y los delegados. La diferencia entre los dos, así como en el nivel de aplicación, depende sustancialmente de "cuánto" - objetos - puede recibir un mensaje. Primero deja que te enseñe cómo lo hace el concepto de delegado.
Delegar
Imaginemos que tenemos nuestra clase habitual A (nuestro receptor, en este ejemplo), que crea una instancia de un objeto de la clase B (el transmisor). La clase A quiere ser notificado cuando una determinada operación, llevada a cabo por la clase B, se ha completado. Una implementación de crudo de este concepto, totalmente legítima y en funcionamiento, sería: Clase A pasa un puntero a la clase B, de modo que cuando éste quiere "advertir" a la Clase A no deberá dejar de invocar un método mediante el puntero a su pasado.

El código de la clase B, entonces, sería:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | / / ------------------------------------------------ ---------------------- / / Archivo ClassB.h @ Clase ClassA; NSObject { @ Interface ClassB: NSObject { ClassA * pointerClassA; } / / Yo proporciono una propiedad con la que la clase A puede / / Pasar el puntero assign ) ClassA * pointerClassA; @ Property (asignar) ClassA * pointerClassA; @ End / / ------------------------------------------------ ---------------------- / / ClassB.m archivo # Importar "ClassB.h" # Importar "ClassA.h" @ Implementación ClassB @ Sintetizar pointerClassA; void ) operazione { - (Void) {operación / / ... operación / / Enviar la Clase Un aviso de término ; [PointerClassA metodoDiAvviso]; } @ End |
Clase A Clase B sabe que acepta un puntero a su (Clase A y Clase B se conocen entre sí, pero ... lo que necesita saber en este ejemplo) también sabe que cuando el B-Class habrá completado su tarea invocará un método llamado metodoDiAvviso . Por lo tanto, la clase A, que tiene que ver que para aplicar este método y pasar una Clase B de su puntero. Todo esto se tradujo en código, es:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | / / ------------------------------------------------ ---------------------- / / Archivo ClassA.h @ Clase ClassB; <NSObject> { @ Interface ClassA: {<NSObject> ClassB * ClassB; } void ) metodoDiAvviso; - (Void) metodoDiAvviso; void ) metodoQualsiasi; - (Void) metodoQualsiasi; @ End / / ------------------------------------------------ ---------------------- / / ClassA.m archivo # Importar "ClassA.h" # Importar "ClassB.h" @ Implementación ClassA id ) init { - (Id) init { super init ] ; self = [super init]; self ) { if (auto) { ClassB alloc ] init ] ; ClassB = [[ClassB alloc] init]; classB.pointerA = yo / / I comunicar mi puntero a la Clase B } volver yo; } void ) metodoQualsiasi { - (Void) {metodoQualsiasi / / ... ; [ClassB operación]; } void ) metodoDiAvviso { - (Void) {metodoDiAvviso / / Método invocado por la Clase B "%s: la Classe B ha terminato e mi ha avvisato!" , __FUNCTION__ ) ; NSLog (@ "% s: el B-Class ha terminado y me advirtió", __ FUNCTION__); } @ End |
Aunque esta técnica funciona, muestra todas sus limitaciones cuando se usan en contextos también pequeñas. Vamos a ver cuáles son las cosas que no son:
- Las clases deben conocerse entre sí: cada uno ha importado las definiciones de la otra
- El A-Class debe aplicar y declarar el receptor método
- La Clase B se supone que hay un Clase A, es decir, el puntero
pointerAser explotado - Todo está hecho explícitos los nombres de las clases en el juego
- Si desea enviar un mensaje de Clase B a la Clase C debe ser: o cambiar el código o duplicar el tour
Afortunadamente, hay una manera mucho más elegante, clara y fácil de usar para hacer frente a estas situaciones. Ya hemos hablado acerca de cómo crear un delegado con su propio protocolo . Objective-C permite definir un protocolo de comunicación a través del cual una o más clases pueden intercambiar información.
Nota: Es posible que los protocolos de Objective-C se indican como una manera de heredar la interfaz (no la aplicación) de otras clases. Esto es cierto, aunque hay algunas diferencias sustanciales en este caso en comparación con lo que ocurre con otros idiomas.
En nuestro caso, el protocolo debe estar definido dentro de la Clase B, Clase A y aprobada por la En la práctica, una clase define lo que es el protocolo de comunicación. Las clases que tengo la intención de recibir los mensajes de este último, se adoptan los protocolos.
Repasemos el ejemplo anterior y vuelva a grabar utilizando protocolos y delegados:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | / / ------------------------------------------------ ---------------------- / / Archivo ClassB.h @ ClassBDelegate Protocolo; NSObject { @ Interface ClassB: NSObject { Identificación <ClassBDelegate> delegada; } / / Delegado assign ) id<ClassBDelegate> delegate; @ Property (asignar) Identificación <ClassBDelegate> delegada; @ End @ Protocolo ClassBDelegate <NSObject> @ Opcional / / Mensaje enviado al delegado cuando la operación void ) metodoDiAvviso; - (Void) metodoDiAvviso; @ End / / ------------------------------------------------ ---------------------- / / ClassB.m archivo # Importar "ClassB.h" @ Implementación ClassB @ Sintetizar delegado; void ) operazione { - (Void) {operación / / ... operación / / Si hay un delegado y delegado que cumpla con el protocolo / / ClassBDelegate enviar notificaciones de acabado delegate && [ delegate respondsToSelector : @selector ( metodoDiAvviso ) ] ) { if (&& delegado [delegado respondsToSelector: @ selector (metodoDiAvviso)]) { ; [Delegada metodoDiAvviso]; } } @ End |
En el nuevo Código, que ya se nota un avance significativo: no hay ninguna indicación en la Clase A. Este último, por su parte, en vez debería adoptar el protocolo explícitamente puesto a disposición por la Clase B, si quiere ser un delegado y recibir notificaciones:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | / / ------------------------------------------------ ---------------------- / / Archivo ClassA.h # Importar "ClassB.h" / / adopto el ClassBDelegate protocolo < NSObject , ClassBDelegate> { @ Interface ClassA: < NSObject , ClassBDelegate> { ClassB * ClassB; } void ) metodoQualsiasi; - (Void) metodoQualsiasi; @ End / / ------------------------------------------------ ---------------------- / / ClassA.m archivo # Importar "ClassA.h" @ Implementación ClassA id ) init { - (Id) init { super init ] ; self = [super init]; self ) { if (auto) { ClassB alloc ] init ] ; ClassB = [[ClassB alloc] init]; / / Comunicarse con la Clase B sea el que su suplente classB.delegate = sí; } volver yo; } void ) metodoQualsiasi { - (Void) {metodoQualsiasi / / ... ; [ClassB operación]; } / / De acuerdo con el protocolo aprobado void ) metodoDiAvviso { - (Void) {metodoDiAvviso / / Método invocado por la Clase B "%s: la Classe B ha terminato e mi ha avvisato!" , __FUNCTION__ ) ; NSLog (@ "% s: el B-Class ha terminado y me advirtió", __ FUNCTION__); } @ End |
Este proceso es mucho más limpio y más sencillo de implementar. Las ventajas son:
- La clase que desea delegar (Clase B) no debe conocer a sus delegados, lo suficiente para él y cuando cumplan con su protocolo. De esta manera, cualquier clase puede recibir mensajes
- La clase que desea recibir el mensaje (Clase A) adopta explícitamente el / i protocolo / s, que se puede tomar en parte o en su totalidad en función de cómo haya definido
El único problema con este enfoque se relaciona con el número de delegados. En nuestro ejemplo, el caso de la Clase B fue creado para toda la clase A. En este contexto, es obvio (pero no necesariamente) que es la Clase A a ser el delegado. En una variante diferente puede crear una instancia de clase A Clase C y suministrarla como delegado a la Clase B (Clase C tendrá que adoptar el ClassBDelegate Protocol). Sin embargo, lo que está claro es que una instancia está ligada solo delegado, Clase B no puede tener dos o más delegados, no proporcionan ninguna propiedad por valor de delegate .
Denominación
Disfrute de sus palabras sobre la denominación, es decir, sobre el nombramiento de los protocolos y métodos de esta última. El nombre del protocolo, si quiere seguir en el Apple reglas, se utiliza normalmente para pasar el Delegate al final del nombre de la clase que define el protocolo. En otros casos, cuando el protocolo no está ligado a ninguna clase en particular, utiliza el truco ...ing Inglés, que no debe confundirse con la clase de protocolo. Así, por ejemplo:
1 2 3 4 | NSLook se NSLooking, NSLook parece ser una clase ClassB definir su ClasseBDelegate protocolo UIWebView define UIWebViewDelegate ... |
El método definido en el protocolo, en cambio, sigue la regla que siempre se debe pasar el puntero de quien envió el mensaje, seguido de los parámetros de su caso. Por ejemplo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | BOOL ) tableView : ( NSTableView * ) tableView shouldSelectRow : ( int ) row; - (BOOL) tableView: ( NSTableView *) tableView shouldSelectRow: (int) fila; BOOL ) application : ( NSApplication * ) sender openFile : ( NSString * ) filename; - (BOOL) aplicación: ( NSApplication *) emisor abrirArchivo: ( NSString *) nombre del archivo; / / Si no hay parámetros BOOL ) applicationOpenUntitledFile : ( NSApplication * ) sender; - (BOOL) applicationOpenUntitledFile: ( NSApplication *) remitente; / / Utilizamos "Did" o "voluntad" para notificar al delegado que algo es / / El éxito o de que algo va a suceder void ) browserDidScroll : ( NSBrowser * ) sender; - (Void) browserDidScroll: ( NSBrowser *) remitente; NSUndoManager * ) windowWillReturnUndoManager : ( NSWindow * ) window; - ( NSUndoManager *) windowWillReturnUndoManager: ( NSWindow *) ventana; / / El prefijo "debería" se utiliza en lugar de pedir al delegado / / Si quieres que algo suceda BOOL ) windowShouldClose : ( id ) sender; - (BOOL) windowShouldClose: (id) del remitente; |
Notificaciones
Cuando un objeto o una instancia de una clase pueden ser compartidos por varios objetos y usted tiene la necesidad de alertar a todos los objetos afectados, se debe utilizar un método diferente para notificar mensajes, las llamadas notificaciones.
Notificaciones y protocolos / delegados pueden coexistir fácilmente en el mismo código, lo que significa que si ya se ha implementado un protocolo / un delegado, no hay nada que añadir notificaciones. entonces deja que te enseñe un ejemplo usando sólo sus notificaciones iniciales.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | / / ------------------------------------------------ ---------------------- / / Archivo ClassB.h NSObject { @ Interface ClassB: NSObject { } @ End / / ------------------------------------------------ ---------------------- / / ClassB.m archivo # Importar "ClassB.h" @ Implementación ClassB @ Sintetizar pointerClassA; void ) operazione { - (Void) {operación / / ... operación / / Enviar una notificación a todos los que se han registrado defaultCenter ] postNotificationName : @ "operazioneTerminata" ] ; [[ NSNotificationCenter defaultCenter] postNotificationName: @ "operazioneTerminata"]; } @ End |
Como puede verse en el código anterior, esta técnica no añade nada, que no sea el envío de la notificación. Los que quieren ser notificado tendrá que hacer muy poco:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | / / ------------------------------------------------ ---------------------- / / Archivo ClassA.h <NSObject> { @ Interface ClassA: {<NSObject> ClassB * ClassB; } void ) metodoQualsiasi; - (Void) metodoQualsiasi; @ End / / ------------------------------------------------ ---------------------- / / ClassA.m archivo # Importar "ClassA.h" @ Implementación ClassA id ) init { - (Id) init { super init ] ; self = [super init]; self ) { if (auto) { ClassB alloc ] init ] ; ClassB = [[ClassB alloc] init]; / / Me registro como interesados en el mensaje "operazioneTerminata" defaultCenter ] addObserver : self [[ NSNotificationCenter defaultCenter] addObserver: self ( notificaOperazioneTerminata ) selector: @ selector (notificaOperazioneTerminata) "operazioneTerminata" Nombre: @ "operazioneTerminata" ] ; objeto: nil]; } volver yo; } void ) metodoQualsiasi { - (Void) {metodoQualsiasi / / ... ; [ClassB operación]; } / / El nombre de este método decide la Clase A y puede ser / / Entonces, cualquier void ) notificaOperazioneTerminata { - (Void) {notificaOperazioneTerminata / / Método invocado por la Clase B "%s: la Classe B ha terminato e mi ha avvisato!" , __FUNCTION__ ) ; NSLog (@ "% s: el B-Class ha terminado y me advirtió", __ FUNCTION__); } void ) dealloc { - (Void) dealloc { / / Quito a mí mismo de la recepción de las notificaciones relativas a la / / Mensaje identificado por la cadena @ "operazioneTerminata" defaultCenter ] removeObserver : self [[ NSNotificationCenter defaultCenter] removeObserver: self "operazioneTerminata" Nombre: @ "operazioneTerminata" ] ; objeto: nil]; ; [Súper dealloc]; } @ End |
Denominación
Aquí, también, unas palabras sobre el nombramiento de las notificaciones, siempre de acuerdo con la sugerencia de Apple. El nombre de la notificación, que es un NSString , se construye normalmente de acuerdo con este criterio:
1 | + [ Did | Will ] + [ UniquePartOfName ] + Notification [Nombre de la clase asociada] + [DID | Will] + [UniquePartOfName] + Notificación |
En el ejemplo anterior, tendríamos que poner en el archivo H Clase B.:
1 2 3 4 5 6 7 8 9 10 | / / En el archivo de encabezado de la clase B * const ClassBDidFinishedNotification; extern NSString * const ClassBDidFinishedNotification; / / En el archivo de implementación de la Clase B const ClassBDidFinishedNotification = @ "ClassBDidFinishedNotification" ; NSString * const ClassBDidFinishedNotification = @ "ClassBDidFinishedNotification"; / / El método de la clase A, sin embargo, es más libre, pero puede ser escrito / / Por ejemplo añadiendo "Notificación" al final - esto, sin embargo, tiene menos / / Importancia void ) finishedNotification; - (Void) finishedNotification; |
Parámetros
Ambas técnicas descritas anteriormente permiten enviar mensajes con número arbitrario de parámetros. Tomamos el ejemplo anterior y tratar de pasar dos parámetros. En el caso del protocolo / delegado, podríamos tener:
1 2 3 4 5 | @ Protocolo ClassBDelegate <NSObject> @ Opcional / / Mensaje enviado al delegado cuando la operación void ) classB : ( ClassB * ) aClassB avvisoConID : ( int ) aID; - (Void) ClassB: (ClassB *) aClassB avvisoConID: (int) de la ayuda; @ End |
La aplicación debería haberse escrito:
1 2 3 4 5 6 7 8 | void ) operazione { - (Void) {operación / / ... operación / / Si hay un delegado y delegado que cumpla con el protocolo / / ClassBDelegate enviar notificaciones de acabado delegate && [ delegate respondsToSelector : @selector ( classB : avvisoConID : ) ] ) { if (&& delegado [delegado respondsToSelector: @ selector (ClassB: avvisoConID :)]) { self avvisoConID : 57 ] ; [ClassB delegado: self avvisoConID: 57]; } } |
Como se puede ver muy, muy simple.
) contiene un puntatore ad un NSDictionary con la collezione dei parametri. Para las notificaciones, el discurso es similar, aunque en este caso se utiliza un solo parámetro userInfo que normalmente (si no nil ) contiene un puntero a un NSDictionary con la colección de parámetros. En el momento en que la notificación habría tenido:
1 2 3 4 5 6 7 8 | void ) operazione { - (Void) {operación / / ... operación / / Enviar una notificación a todos los que se han registrado defaultCenter ] postNotificationName : @ "operazioneTerminata" [[ NSNotificationCenter defaultCenter] postNotificationName: @ "operazioneTerminata" objeto: self NSDictionary dictionaryWithObject : [ NSNumber numberWithInt : 57 ] userInfo: [ NSDictionary dictionaryWithObject: [ NSNumber numberWithInt: 57] "avvisoConID" ] ] ; forKey: @ "avvisoConID"]]; } |
En el momento de la lectura de la notificación:
1 2 3 4 5 6 7 | void ) notificaOperazioneTerminata : ( NSNotification * ) aNotification { - (Void) notificaOperazioneTerminata: ( NSNotification *) {aNotification / / Método invocado por la Clase B "%s: la Classe B ha terminato e mi ha avvisato!" , __FUNCTION__ ) ; NSLog (@ "% s: el B-Class ha terminado y me advirtió", __ FUNCTION__); avvisoConID = [ [ aNotification userInfo ] objectForKey : @ "avvisoConID" ] ; NSNumber * avvisoConID = [[aNotification userInfo] objectForKey: @ "avvisoConID"]; "%s: %d" , __FUNCTION__, [ avvisoConID intValue ] ) ; NSLog (@ "% s:% d", __ FUNCTION__, [avvisoConID intValue]); } |
En conclusión, al utilizar delegados / protocolos y las notificaciones?
Como hemos visto las dos soluciones son simples, de gran alcance y fácil de implementar. Si los eventos de la instancia de nuestra clase puede afectar a múltiples objetos, no tenemos más remedio que utilizar las notificaciones. En todos los demás casos, los delegados / protocolos o ambas soluciones.
Normalmente recomendamos a partir de la aplicación de los protocolos y de los delegados, como si sucediera que un evento es de interés colectivo, siempre se puede enviar en la notificación.










Excelente artículo!
Claro, preciso y lleno de ejemplos!
Blog muy interesante!
Saludos
No es que usted podría tener un último ejemplo de la utilización de los delegados? Aquí nos limitamos a la definición, pero un ejemplo práctico de cómo usarlo (en referencia al código del ejemplo)?
[...] La cuestión de ilew con un artículo genuino para explicar mejor, ejemplo adjuntando, cómo funcionan y los delegados [...]
@ Ilew: sin duda, ver aquí
muy clara y sencilla
Debo admitir que la escritura de un pa apenas utilice delegados creados por mí ... por supuesto que es una técnica que me he comprometido a utilizar con más frecuencia, aunque forzando la necesidad
felicitaciones