¿Cómo creo delegados en Objective-C?

Resuelto Andy Jacobs asked hace 54 años • 20 respuestas

Sé cómo funcionan los delegados y sé cómo puedo utilizarlos.

¿Pero cómo los creo?

Andy Jacobs avatar Jan 01 '70 08:01 Andy Jacobs
Aceptado

Un delegado de Objective-C es un objeto al que se le ha asignado la delegatepropiedad de otro objeto. Para crear uno, define una clase que implementa los métodos delegados que le interesan y marca esa clase como que implementa el protocolo delegado.

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

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

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

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

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

Por otro UIWebViewlado, probablemente tenga un código similar a este para ver si el delegado responde al webViewDidStartLoad:mensaje usando respondsToSelector:y enviarlo si corresponde.

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

La propiedad delegada en sí generalmente se declara weak(en ARC) o assign(pre-ARC) para evitar bucles de retención, ya que el delegado de un objeto a menudo tiene una fuerte referencia a ese objeto. (Por ejemplo, un controlador de vista suele ser el delegado de una vista que contiene).

Hacer delegados para tus clases

Para definir sus propios delegados, deberá declarar sus métodos en algún lugar, como se explica en Apple Docs sobre protocolos . Generalmente declaras un protocolo formal. La declaración, parafraseada de UIWebView.h, tendría este aspecto:

@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, UIWebViewDelegateen 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 delegados), debe consultar -respondsToSelector:antes de llamar a un método en particular.

Nombrar

Los métodos delegados normalmente reciben nombres que comienzan con el nombre de la clase que delega y toman el objeto que delega como primer parámetro. También suelen utilizar una forma de voluntad, debería o hizo. Entonces, webViewDidStartLoad:(el primer parámetro es la vista web) en lugar de loadStarted(sin tomar parámetros), por ejemplo.

Optimizaciones de velocidad

En lugar de verificar si un delegado responde a un selector cada vez que queremos enviarle un mensaje, puede almacenar en caché esa información cuando se configuran los delegados. Una forma muy clara 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 maneja los mensajes accediendo a nuestra delegateRespondsToestructura, en lugar de enviarlos -respondsToSelector:una y otra vez.

Delegados informales

Antes de que existieran los protocolos, era común usar una categoría para NSObjectdeclarar los métodos que un delegado podía implementar. Por ejemplo, CALayertodavía hace esto:

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

Esto le dice al compilador que cualquier objeto podría implementarlo displayLayer:.

Luego utilizaría el mismo -respondsToSelector:enfoque descrito anteriormente para llamar a este método. Los delegados implementan este método y asignan la delegatepropiedad, y eso es todo (no hay que declarar que usted se ajusta a un protocolo). Este método es común en las bibliotecas de Apple, pero el nuevo código debería utilizar el enfoque de protocolo más moderno anterior, ya que este enfoque contamina NSObject(lo que hace que el autocompletado sea menos útil) y dificulta que el compilador le advierta sobre errores tipográficos y similares.

Jesse Rusak avatar Mar 09 '2009 16:03 Jesse Rusak

La respuesta aprobada es excelente, pero si buscas una respuesta de 1 minuto, prueba esto:

El archivo MyClass.h debería verse así (¡agregue 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
}

MiVC.m:

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

Implementar el método delegado

- (void) myClassDelegateMethod: (MyClass *) sender {
    NSLog(@"Delegates are great!");
}
Tibidabo avatar Sep 30 '2012 10:09 Tibidabo

Al utilizar el método de protocolo formal para crear soporte para delegados, descubrí que puede garantizar una verificación de tipo adecuada (aunque en tiempo de ejecución, no en tiempo 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 delegado (setDelegate). Esto ayuda a minimizar los errores.

umop avatar May 04 '2010 20:05 umop