2010-02-09 14 views
32

Estoy tratando de llamar a un método en mi controlador de vista raíz desde un controlador de vista infantil de modo que cuando cambie mis opciones actualizarán automáticamente la vista raíz, que a su vez actualizará muchos otros controladores de vista. Para la segunda parte he usado notificaciones, pero para esta primera estoy tratando de usar un delegado porque (así me han hecho creer) es una buena práctica de programación. Tengo problemas para hacerlo funcionar y sé que puedo configurar otra notificación fácilmente para hacer el trabajo. ¿Debo seguir intentando implementar el delegado o simplemente usar una notificación?Delegados vs. Notificaciones en iPhoneOS

Respuesta

57

Delegar es una buena práctica de programación para muchas situaciones, pero eso no significa que deba usarla si no se siente cómodo con ella. Tanto la delegación como las notificaciones ayudan a desacoplar los controladores de vista entre sí, lo cual es algo bueno. Las notificaciones pueden ser un poco más fáciles de codificar y ofrecen la ventaja de que varios objetos pueden observar una notificación. Con los delegados, tal cosa no se puede hacer sin modificar el objeto delegante (y es inusual).

Algunas ventajas de delegar:

  • La conexión entre el objeto y delegar delegado se hizo más clara, sobre todo si la ejecución del delegado es obligatorio.
  • Si se debe pasar más de un tipo de mensaje de delegado a delegado, la delegación puede aclarar esto al especificar un método de delegado por mensaje. Para las notificaciones, puede usar varios nombres de notificación, pero todas las notificaciones terminan en el mismo método del lado del observador (lo que posiblemente requiera una desagradable instrucción de cambio).

Solo usted puede decidir qué patrón es más apropiado para usted. En cualquier caso, debería considerar no hacer que su controlador de vista envíe la notificación o el mensaje de delegado.En muchos casos, el controlador de vista debe cambiar el modelo y luego el modelo debe informar a sus observadores o su delegado que se ha cambiado.

La implementación de un patrón de delegado es simple:

  1. En su ChildViewController.h, declaran el protocolo delegado que el delegado debe implementar más adelante:

    @protocol ChildViewControllerDelegate <NSObject> 
    @optional 
    - (void)viewControllerDidChange:(ChildViewController *)controller; 
    @end 
    
  2. En la parte superior del archivo, crear una variable de instancia para mantener el puntero al delegado en su ChildViewController:

    @protocol ChildViewControllerDelegate; 
    @interface ChildViewController : UIViewController { 
        id <ChildViewControllerDelegate> delegate; 
        ... 
    } 
    @property (assign) id <ChildViewControllerDelegate> delegate; 
    ... 
    @end 
    
  3. En RootViewController.h, hacer que su clase conforme con el protocolo de delegado:

    @interface RootViewController : UIViewController <ChildViewControllerDelegate> { 
    ... 
    
  4. En la implementación RootViewController, poner en práctica el método delegado. Además, cuando crea la instancia de ChildViewController, debe asignar el delegado.

    @implement RootViewController 
    ... 
    // in some method: 
    ChildViewController *controller = [[ChildViewController alloc] initWithNibName:... 
    controller.delegate = self; 
    ... 
    - (void)viewControllerDidChange:(ChildViewController *)controller { 
        NSLog(@"Delegate method was called."); 
    } 
    ... 
    
  5. En la implementación ChildViewController, llame al método delegado en el momento apropiado:

    @implementation ChildViewController 
    ... 
    // in some method: 
    if ([self.delegate respondsToSelector:@selector(viewControllerDidChange:)]) { 
        [self.delegate viewControllerDidChange:self]; 
    } 
    ... 
    

Eso es todo. (Nota: He escrito esto desde la memoria, así que probablemente haya algunos errores tipográficos).

+0

Una gran respuesta, estaba a punto de escribir la mía, pero la tuya es realmente buena. –

+0

Gracias Oscar. –

+0

Fantástico, ese fue el truco. Cuando recién comenzaba con iPhone OS, asistí a uno de esos seminarios para desarrolladores que tuve Apple y la mayor parte desafortunadamente se me pasó por la cabeza en ese momento. Había un diagrama específico que mostraron sobre esto y ahora finalmente lo entiendo. Muchas gracias. –

2

Los delegados son un poco difíciles de acostumbrar, pero creo que es la mejor práctica y, como Apple, solo funcionan.

Siempre uso la declaración de protocolo formal. Es un poco más lógico en mi mente, y está muy claro en el código. Sugiero usar una UIView para cambiar sus opciones en lugar de un controlador. Siempre uso un controlador principal y tengo muchas UIViews subclasificadas que el único controlador puede controlar. (Sin embargo, puede modificar el código siguiente para un controlador, si realmente necesita un controlador en lugar de una vista normal.) En el archivo de cabecera de la vista del niño, hacer que se vea como esto:

// ChildView.h 
#import <UIKit/UIKit.h> 

@protocol ChildViewDelegate; // tells the compiler that there will be a protocol definition later 

@interface ChildViewController : UIView { 
    id <ChildViewDelegate> delegate; 
    // more stuff 
} 

// properties and class/instance methods 

@end 

@protocol ChildViewDelegate // this is the formal definition 

- (void)childView:(ChildView *)c willDismissWithButtonIndex:(NSInteger)i; // change the part after (ChildView *)c to reflect the chosen options 

@end 

El método entre @protocol y el segundo @end se puede llamar en algún lugar de la implementación de ChildView, y luego su controlador de vista raíz puede ser el delegado que recibe la 'notificación'.

El archivo .m debería ser así:

// ChildView.m 
#import "ChildView.h" 

@implementation ChildView 

- (id)initWithDelegate:(id<ChildViewDelegate>)del { // make this whatever you want 
    if (self = [super initWithFrame:CGRect(0, 0, 50, 50)]) { // if frame is a parameter for the init method, you can make that here, your choice 
     delegate = del; // this defines what class listens to the 'notification' 
    } 
    return self; 
} 

// other methods 

// example: a method that will remove the subview 

- (void)dismiss { 
    // tell the delegate (listener) that you're about to dismiss this view 
    [delegate childView:self willDismissWithButtonIndex:3]; 
    [self removeFromSuperView]; 
} 

@end 

Entonces archivo .h del controlador de vista raíz, incluya el siguiente código:

// RootViewController.h 
#import "ChildView.h" 

@interface RootViewController : UIViewController <ChildViewDelegate> { 
    // stuff 
} 

// stuff 

@end 

y el archivo de aplicación implementará el método definido en el protocolo en ChildView.h, porque se ejecutará cuando ChildView llame para que se ejecute. En ese método, coloca las cosas que suceden cuando recibes la notificación.

+2

Mi entendimiento es que muchas subclases de UIViewController sería el mejor enfoque y el UIView subclases deben limitarse a cuando una vista personalizada (anulando 'drawRect:') que se necesita. – gerry3

10

Normalmente, si necesita actualizar la interfaz de usuario según un cambio en los datos de un modelo, tendría los controladores de vista observe los datos relevantes del modelo y actualice sus puntos de vista cuando se le notifiquen los cambios.

veo delegación como un poco más formal y al igual que la distinción que Peter Hosey recientemente compartió:

La diferencia es que la delegación es de a-uno (y bidireccional) la comunicación, mientras que las notificaciones son para a-muchos, unidireccional comunicación.

Además, he encontrado que (por completo) la actualización de la vista en viewWillAppear: funciona bien (pero esto no es la mejor solución donde el rendimiento es una preocupación).

0

En este caso, no necesita usar ninguna delegación o notificación porque realmente no necesita comunicarse directamente entre sus vistas. Como dijo gerry3, debe cambiar el modelo de datos en sí mismo y luego dejar que todas las demás vistas respondan a ese cambio.

Su modelo de datos debe ser un objeto independiente al que todos sus controladores de vista tengan acceso. (La manera perezosa es estacionarlo como un atributo del delegado de la aplicación.) Cuando el usuario realiza un cambio en la Vista A, el controlador de la Vista A escribe que cambia al modelo de datos. Luego, cada vez que se abren las Vistas B a la Z, los controladores leen el modelo de datos y configuran las vistas de manera apropiada.

De esta manera, ni las vistas, ni sus controladores deben conocerse entre sí y todos los cambios ocurren en un objeto central para que sean fácilmente rastreados.

+0

Las notificaciones son una buena manera de evitar la sobrecarga de muchas cosas sondear repetidamente, porque aunque sólo cambian cuando necesitan ... –

+0

Sí, pero en este caso los controladores de vista debe estar leyendo el modelo de datos cuando muestran la vista. Como solo un controlador está activo en un momento dado, no hay ninguna razón para que los controladores consulten el modelo de datos. Ahora es el modelo de datos en flujo constante de, digamos, una entrada de url, entonces lo harían, pero eso rara vez es el caso. – TechZen

0

¿Es realmente necesario para el controlador de vista raíz para saber acerca de los cambios, o sólo los subvistas?

Si el controlador de raíz no tiene por qué saber, tener la configuración de enviar las notificaciones de los otros puntos de vista están buscando que parece ser una respuesta mejor para mí, ya que simplifica código. No es necesario introducir más complejidad de la que debe.

23

me gustaría añadir:

objetos que reciben notificaciones pueden reaccionar sólo después de que se ha producido el evento . Esta es una diferencia significativa de de la delegación. El delegado tiene la posibilidad de rechazar o modificar la operación propuesta por el objeto delegado. Observando objetos , por otro lado, no puede afectar directamente a una operación inminente .

5

notificaciones pueden hacer que el comportamiento de ejecución de su programa significativamente más complejo. Piense en ello como un goto con múltiples destinos. El orden de esos destinos no está definido. Si alguna vez falla, hay poca información de seguimiento de la pila.

hay casos en los que tiene sentido utilizar las notificaciones - el típico ser para comunicar un cambio de modelo o un cambio de estado global a sus puntos de vista. Ejemplo, la red está inactiva, la aplicación va a renunciar, etc.

Vale la pena reconocer el patrón delegado en IOS. Los delegados le dan rastros de pila completos cuando depura. Resultan en un comportamiento de tiempo de ejecución significativamente más simple al mismo tiempo que logran el objetivo de desacoplar sus objetos.