Pregunta ¿Cómo creo delegados en Objective-C?


Sé cómo trabajan los delegados, y sé cómo puedo usarlos.

¿Pero cómo los creo?


689
2018-03-09 16:06


origen


Respuestas:


Un delegado de Objective-C es un objeto que ha sido asignado a la delegate propiedad otro objeto. Para crear uno, simplemente defina una clase que implemente los métodos de delegado que le interesen, y marque esa clase como implementando el protocolo de delegado.

Por ejemplo, supongamos que tiene un UIWebView. Si desea implementar su delegado webViewDidStartLoad: método, podría crear una clase como esta:

@interface MyClass<UIWebViewDelegate>
// ...
@end

@implementation MyClass
- (void)webViewDidStartLoad:(UIWebView *)webView { 
    // ... 
}
@end

Luego, podría crear una instancia de MyClass y asignarla como delegado de la vista web:

MyClass *instanceOfMyClass = [[MyClass alloc] init];
myWebView.delegate = instanceOfMyClass;

Sobre el UIWebView lado, probablemente tiene un código similar a este para ver si el delegado responde a la webViewDidStartLoad: mensaje usando respondsToSelector: y enviarlo si es apropiado.

if([self.delegate respondsToSelector:@selector(webViewDidStartLoad:)]) {
    [self.delegate webViewDidStartLoad:self];
}

La propiedad de delegado en sí misma suele declararse weak (en ARC) o assign (pre-ARC) para evitar retener bucles, ya que el delegado de un objeto a menudo tiene una fuerte referencia a ese objeto. (Por ejemplo, un controlador de vista es a menudo el delegado de una vista que contiene).

Hacer delegados para sus clases

Para definir sus propios delegados, tendrá que declarar sus métodos en algún lugar, como se discutió en el Apple Docs en protocolos. Por lo general, declaras un protocolo formal. La declaración, parafraseada de UIWebView.h, se vería así:

@protocol UIWebViewDelegate <NSObject>
@optional
- (void)webViewDidStartLoad:(UIWebView *)webView;
// ... other methods here
@end

Esto es análogo a una interfaz o clase base abstracta, ya que crea un tipo especial para su delegado, UIWebViewDelegate en este caso. Los implementadores delegados tendrían que adoptar este protocolo:

@interface MyClass <UIWebViewDelegate>
// ...
@end

Y luego implementar los métodos en el protocolo. Para los métodos declarados en el protocolo como @optional (como la mayoría de los métodos de delegado), debe verificar con -respondsToSelector: antes de llamar a un método particular en él.

Nombrando

Los métodos delegados normalmente se nombran comenzando con el nombre de la clase delegante y toman el objeto delegante como primer parámetro. También suelen utilizar una forma de voluntad, debería o no. Asi que, webViewDidStartLoad: (primer parámetro es la vista web) en lugar de loadStarted (sin tomar ningún parámetro) por ejemplo.

Optimizaciones de velocidad

En lugar de verificar si un delegado responde a un selector cada vez que queremos enviar un mensaje, puede almacenar esa información en caché cuando se configuran los delegados. Una manera muy limpia de hacer esto es usar un campo de bits, de la siguiente manera:

@protocol SomethingDelegate <NSObject>
@optional
- (void)something:(id)something didFinishLoadingItem:(id)item;
- (void)something:(id)something didFailWithError:(NSError *)error;
@end

@interface Something : NSObject
@property (nonatomic, weak) id <SomethingDelegate> delegate;
@end

@implementation Something {
  struct {
    unsigned int didFinishLoadingItem:1;
    unsigned int didFailWithError:1;
  } delegateRespondsTo;
}
@synthesize delegate;

- (void)setDelegate:(id <SomethingDelegate>)aDelegate {
  if (delegate != aDelegate) {
    delegate = aDelegate;

    delegateRespondsTo.didFinishLoadingItem = [delegate respondsToSelector:@selector(something:didFinishLoadingItem:)];
    delegateRespondsTo.didFailWithError = [delegate respondsToSelector:@selector(something:didFailWithError:)];
  }
}
@end

Luego, en el cuerpo, podemos verificar que nuestro delegado maneje los mensajes accediendo a nuestro delegateRespondsTo struct, en lugar de enviar -respondsToSelector: una y otra vez.

Delegados informales

Antes de que existieran los protocolos, era común usar un categoría en NSObject declarar los métodos que un delegado podría implementar. Por ejemplo, CALayer todavía hace esto:

@interface NSObject(CALayerDelegate)
- (void)displayLayer:(CALayer *)layer;
// ... other methods here
@end

Esto básicamente le dice al compilador que cualquier objeto podría implementar displayLayer:.

Entonces usarías el mismo -respondsToSelector: enfoque como se describe anteriormente para llamar a este método. Los delegados simplemente implementan este método y asignan el delegatepropiedad, y eso es todo (no hay declaración de que se ajuste a un protocolo). Este método es común en las bibliotecas de Apple, pero el nuevo código debe usar el enfoque de protocolo más moderno anterior, ya que este enfoque contamina NSObject (lo que hace que Autocomplete sea menos útil) y hace que sea difícil para el compilador advertirle sobre errores tipográficos y errores similares.


853
2018-03-09 16:16



La respuesta aprobada es excelente, pero si está buscando una respuesta de 1 minuto, intente esto:

El archivo MyClass.h debería verse así (¡agrega líneas de delegado con comentarios!)

#import <BlaClass/BlaClass.h>

@class MyClass;             //define class, so protocol can see MyClass
@protocol MyClassDelegate <NSObject>   //define delegate protocol
    - (void) myClassDelegateMethod: (MyClass *) sender;  //define delegate method to be implemented within another class
@end //end protocol

@interface MyClass : NSObject {
}
@property (nonatomic, weak) id <MyClassDelegate> delegate; //define MyClassDelegate as delegate

@end

El archivo MyClass.m debería verse así

#import "MyClass.h"
@implementation MyClass 
@synthesize delegate; //synthesise  MyClassDelegate delegate

- (void) myMethodToDoStuff {
    [self.delegate myClassDelegateMethod:self]; //this will call the method implemented in your other class    
}

@end

Para usar su delegado en otra clase (UIViewController llamado MyVC en este caso) MyVC.h:

#import "MyClass.h"
@interface MyVC:UIViewController <MyClassDelegate> { //make it a delegate for MyClassDelegate
}

MyVC.m:

myClass.delegate = self;          //set its delegate to self somewhere

Implementar método delegado

- (void) myClassDelegateMethod: (MyClass *) sender {
    NSLog(@"Delegates are great!");
}

363
2017-09-30 10:25



Cuando uso el método de protocolo formal para crear soporte para delegados, he descubierto que puede garantizar una verificación de tipos adecuada (aunque sea tiempo de ejecución, no de compilación) agregando algo como:

if (![delegate conformsToProtocol:@protocol(MyDelegate)]) {
    [NSException raise:@"MyDelegate Exception"
                format:@"Parameter does not conform to MyDelegate protocol at line %d", (int)__LINE__];
}

en su código de acceso de delegado (setDelegate). Esto ayuda a minimizar los errores.


18
2018-05-04 20:42



Tal vez esto sea más parecido a lo que te estás perdiendo:

Si vienes desde un punto de vista similar a C ++, los delegados tardan un poco en acostumbrarse, pero básicamente "simplemente funcionan".

La forma en que funciona es que estableces algún objeto que escribiste como delegado en NSWindow, pero tu objeto solo tiene implementaciones (métodos) para uno o algunos de los muchos métodos delegados posibles. Entonces algo sucede, y NSWindow quiere llamar a tu objeto, solo usa Objective-c's respondsToSelector método para determinar si su objeto quiere que se llame ese método, y luego lo llama. Así es como funciona Objective-c: los métodos se buscan a demanda.

Es totalmente trivial hacer esto con tus propios objetos, no pasa nada especial, podrías por ejemplo tener un NSArray de 27 objetos, todos los diferentes tipos de objetos, solo 18 algunos de ellos tienen el método -(void)setToBue; Los otros 9 no. Entonces para llamar setToBlue en todos los 18 que lo necesitan, algo como esto:

for (id anObject in myArray)
{
  if ([anObject respondsToSelector:@selector(@"setToBlue")])
     [anObject setToBlue]; 
}

La otra cosa acerca de los delegados es que no se conservan, por lo que siempre tiene que configurar el delegado para nil en tus MyClass dealloc método.


17
2018-03-10 00:18



¡Por favor! compruebe a continuación el sencillo tutorial paso a paso para comprender cómo funcionan los delegados en iOS.

Delegado en iOS

Creé dos ViewControllers (para enviar datos de uno a otro)

  1. FirstViewController implementa delegado (que proporciona datos).
  2. SecondViewController declara el delegado (que recibirá los datos).

17
2018-02-27 12:21



Como buena práctica recomendada por Apple, es bueno para el delegado (que es un protocolo, por definición), ajustarse a NSObject protocolo.

@protocol MyDelegate <NSObject>
    ...
@end

& para crear métodos opcionales dentro de su delegado (es decir, métodos que no necesariamente deben implementarse), puede usar @optional anotación como esta:

@protocol MyDelegate <NSObject>
    ...
    ...
      // Declaration for Methods that 'must' be implemented'
    ...
    ...
    @optional
    ...
      // Declaration for Methods that 'need not necessarily' be implemented by the class conforming to your delegate
    ...
@end

Por lo tanto, cuando utilice métodos que haya especificado como opcionales, deberá verificar (en su clase) respondsToSelector si la vista (que se ajusta a su delegado) realmente ha implementado su (s) método (s) opcional (es) o no.


15
2017-08-04 08:39



Creo que todas estas respuestas tienen mucho sentido una vez que entiendes a los delegados. Personalmente vine de la tierra de C / C ++ y antes de los lenguajes de procedimiento como Fortran, etc. así que aquí está mi búsqueda de 2 minutos para encontrar análogos similares en el paradigma de C ++.

Si tuviera que explicar los delegados a un programador C ++ / Java, diría

Que son los delegados? Estos son punteros estáticos para clases dentro de otra clase. Una vez que asigne un puntero, puede llamar a funciones / métodos en esa clase. Por lo tanto, algunas funciones de su clase son "delegadas" (en C ++ world - puntero a por un puntero de objeto de clase) a otra clase.

¿Qué son los protocolos? Conceptualmente, tiene un propósito similar al del archivo de encabezado de la clase que está asignando como clase de delegado. Un protocolo es una forma explícita de definir qué métodos se deben implementar en la clase cuyo puntero se estableció como un delegado dentro de una clase.

¿Cómo puedo hacer algo similar en C ++? Si intentara hacer esto en C ++, definiría punteros a clases (objetos) en la definición de clase y luego los conectaría a otras clases que proporcionarán funciones adicionales como delegados a su clase base. Pero este cableado debe estar dentro del código y será torpe y propenso a errores. Objective C simplemente asume que los programadores no son los mejores para mantener este descifrado y proporciona restricciones de compilación para aplicar una implementación limpia.


10
2018-06-19 10:34