Objetivo C: ¿Dónde retirar al observador de NSNotification?


Tengo una clase objective C. En él, creé un método init y configuré una NSNotification en él

//Set up NSNotification
[[NSNotificationCenter defaultCenter] addObserver:self 
                                         selector:@selector(getData)
                                             name:@"Answer Submitted"
                                           object:nil];

¿Dónde establezco el [[NSNotificationCenter defaultCenter] removeObserver:self] en esta clase? Sé que para un UIViewController, puedo agregarlo en el método viewDidUnload Así que ¿qué hay que hacer si acabo de crear una clase objective c?

Author: Wayne Burkett, 2011-06-24

14 answers

La respuesta genérica sería "tan pronto como ya no necesite las notificaciones". Obviamente, esta no es una respuesta satisfactoria.

Le recomiendo que agregue una llamada [notificationCenter removeObserver: self] en el método dealloc de esas clases, que tiene la intención de usar como observadores, ya que es la última oportunidad de anular el registro de un observador de forma limpia. Sin embargo, esto solo lo protegerá contra bloqueos debido a que el centro de notificaciones notifica objetos muertos. No puede proteger su código contra la recepción de notificaciones, cuando su los objetos no están todavía / ya no están en un estado en el que puedan manejar correctamente la notificación. Por esto... Véase supra.

Edit (ya que la respuesta parece atraer más comentarios de los que hubiera pensado) Todo lo que estoy tratando de decir aquí es: es realmente difícil dar consejos generales sobre cuándo es mejor eliminar al observador del centro de notificaciones, porque eso depende:

  • En su caso de uso (¿Qué notificaciones se observan? ¿Cuándo los envían?)
  • El implementación del observador (¿Cuándo está listo para recibir notificaciones? ¿Cuándo ya no está listo?)
  • El tiempo de vida previsto del observador (¿Está ligado a algún otro objeto, por ejemplo, una vista o un controlador de vista?)
  • ...

Entonces, el mejor consejo general que se me ocurre: para proteger su aplicación. contra al menos un posible fracaso, haga la danza removeObserver: en dealloc, ya que ese es el último punto (en la vida del objeto), donde puede hacerlo limpiamente. Lo que esto no hace la media es: "solo aplaza la eliminación hasta que se llame dealloc, y todo estará bien". En su lugar, elimine el observador tan pronto como el objeto ya no esté listo (o sea necesario) para recibir notificaciones. Ese es el momento exacto. Desafortunadamente, al no conocer las respuestas a ninguna de las preguntas mencionadas anteriormente, ni siquiera puedo adivinar cuándo sería ese momento.

Siempre se puede con seguridad removeObserver: un objeto varias veces (y todas menos la primera llamada con un observador dado será nops). Por lo tanto: piense en hacerlo (de nuevo) en dealloc solo para estar seguro, pero primero y ante todo: hágalo en el momento apropiado (que está determinado por su caso de uso).

 111
Author: Dirk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-02-27 12:38:36

Nota: Esto ha sido probado y funciona al 100% por ciento

Swift

override func viewWillDisappear(animated: Bool){
    super.viewWillDisappear(animated)

    if self.navigationController!.viewControllers.contains(self) == false  //any other hierarchy compare if it contains self or not
    {
        // the view has been removed from the navigation stack or hierarchy, back is probably the cause
        // this will be slow with a large stack however.

        NSNotificationCenter.defaultCenter().removeObserver(self)
    }
}

PresentedViewController

override func viewWillDisappear(animated: Bool){
    super.viewWillDisappear(animated)

    if self.isBeingDismissed()  //presented view controller
    {
        // remove observer here
        NSNotificationCenter.defaultCenter().removeObserver(self)
    }
}

Objetivo-C

En iOS 6.0 > version , su mejor para eliminar observador en viewWillDisappear como viewDidUnload el método está en desuso.

 [[NSNotificationCenter defaultCenter] removeObserver:observerObjectHere];

Hay muchas veces su mejor a remove observer cuando la vista se ha eliminado de la navigation stack or hierarchy.

- (void)viewWillDisappear:(BOOL)animated{
 if (![[self.navigationController viewControllers] containsObject: self]) //any other hierarchy compare if it contains self or not
    {
        // the view has been removed from the navigation stack or hierarchy, back is probably the cause
        // this will be slow with a large stack however.

        [[NSNotificationCenter defaultCenter] removeObserver:observerObjectHere];
    }
}

PresentedViewController

- (void)viewWillDisappear:(BOOL)animated{
    if ([self isBeingDismissed] == YES) ///presented view controller
    {
        // remove observer here
        [[NSNotificationCenter defaultCenter] removeObserver:observerObjectHere];
    }
}
 38
Author: Paresh Navadiya,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-16 10:03:12

Desde iOS 9 ya no es necesario eliminar observadores.

En OS X 10.11 e iOS 9.0 NSNotificationCenter y NSDistributedNotificationCenter ya no enviará notificaciones a observadores registrados que pueden ser desasignados.

Https://developer.apple.com/library/mac/releasenotes/Foundation/RN-Foundation/index.html#10_11NotificationCenter

 34
Author: Sebastian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-04-24 11:23:35

Si el observador se agrega a un controlador de vista , recomiendo encarecidamente agregarlo en viewWillAppear y eliminarlo en viewWillDisappear.

 25
Author: RickiG,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-12 10:46:53
-(void) dealloc {
      [[NSNotificationCenter defaultCenter] removeObserver:self];
      [super dealloc];
}
 18
Author: Legolas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-06-24 17:38:57

En general lo pongo en el método dealloc.

 7
Author: Raphael Petegrosso,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-06-24 14:31:34

En swift use deinit porque dealloc no está disponible:

deinit {
    ...
}

Documentación de Swift:

Se llama a un deinitializador inmediatamente antes de que una instancia de clase sea desasignado. Escribes deinitializers con la palabra clave deinit, similar cómo se escriben los inicializadores con la palabra clave init. Desinicializadores solo están disponibles en tipos de clase.

Por lo general, no es necesario realizar una limpieza manual cuando su las instancias están desasignadas. Sin embargo, cuando usted está trabajando con los tuyos recursos, es posible que necesite realizar una limpieza adicional a ti mismo. Por ejemplo, si crea una clase personalizada para abrir un archivo y escribir algunos datos en él, es posible que tenga que cerrar el archivo antes de la la instancia de clase está desasignada.

 6
Author: Morten Holmgaard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-22 07:45:34

* editar: Este consejo se aplica a iOS viewWillAppear y eliminar viewWillDisappear - sin embargo, el consejo se aplica si por alguna razón ha agregado el observador en viewDidLoad)

Si has añadido el observador en viewDidLoad deberías eliminarlo tanto en dealloc como en viewDidUnload. De lo contrario, terminará agregándolo dos veces cuando viewDidLoad se llame después de viewDidUnload (esto sucederá después de una advertencia de memoria). Esto no es necesario en iOS 6 donde viewDidUnload está obsoleto y no se llamará (porque las vistas ya no se descargan automáticamente).

 5
Author: Ehren,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-09 16:51:23

En mi opinión, el siguiente código no tiene sentido en ARC :

- (void)dealloc
{
      [[NSNotificationCenter defaultCenter] removeObserver:self];
      [super dealloc];
}

En iOS 6, tampoco tiene sentido eliminar observadores en viewDidUnload, porque ahora ha sido obsoleto.

Para resumir, siempre lo hago en viewDidDisappear. Sin embargo, también depende de tus requisitos, como dijo @Dirk.

 5
Author: kimimaro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-12 10:21:54

Creo que encontré una respuesta confiable! Tuve que hacerlo, ya que las respuestas anteriores son ambiguas y parecen contradictorias. Revisé Libros de Cocina y Guías de Programación.

Primero, el estilo de addObserver: en viewWillAppear: y removeObserver: en viewWillDisappear: no funciona para mí (lo probé) porque estoy publicando una notificación en un controlador de vista hijo para ejecutar código en el controlador de vista padre. Solo usaría este estilo si estuviera publicando y escuchando la notificación dentro de la misma vista controlador.

La respuesta en la que confiaré más, la encontré en la Programación de iOS: Big Nerd Ranch Guide 4th. Confío en los chicos de BNR porque tienen centros de entrenamiento iOS y no solo están escribiendo otro libro de cocina. Probablemente les conviene ser precisos.

BNR ejemplo uno: addObserver: en init:, removeObserver: en dealloc:

BNR ejemplo dos: addObserver: en awakeFromNib:, removeObserver: en dealloc:

When cuando se elimina el observador en dealloc: no se utiliza [super dealloc];

Espero que esto ayuda a la siguiente persona...

Estoy actualizando este post porque Apple ahora ha ido casi por completo con Storyboards por lo que lo mencionado anteriormente puede no aplicarse a todas las situaciones. Lo importante (y la razón por la que agregué este post en primer lugar) es prestar atención si tu viewWillDisappear: está siendo llamado. No fue para mí cuando la aplicación entró en segundo plano.

 4
Author: Murat Zazi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-06 15:25:10

La respuesta aceptada no es segura y podría causar una pérdida de memoria. Por favor deje el unregister en dealloc pero también desregister en viewWillDisappear (eso es, por supuesto, si se registra en viewWillAppear)....ESO ES LO QUE HICE DE TODOS MODOS Y FUNCIONA MUY BIEN! :)

 2
Author: MobileMon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-06-27 20:03:08

Es importante notar también que viewWillDisappear se llama también cuando el controlador de vista presenta una nueva UIView. Este delegado simplemente indica que la vista principal del controlador de vista no está visible en la pantalla.

En este caso, la desasignación de la notificación en viewWillDisappear puede ser inconveniente si estamos utilizando la notificación para permitir que UIView se comunique con el controlador de vista padre.

Como solución suelo quitar al observador en uno de estos dos métodos:

- (void)viewWillDisappear:(BOOL)animated {
    NSLog(@"viewController will disappear");
    if ([self isBeingDismissed]) {
        NSLog(@"viewController is being dismissed");
        [[NSNotificationCenter defaultCenter] removeObserver:self name:@"actionCompleted" object:nil];
    }
}

-(void)dealloc {
    NSLog(@"viewController is being deallocated");
    [[NSNotificationCenter defaultCenter] removeObserver:self name:@"actionCompleted" object:nil];
}

Para razones similares, cuando emito la notificación la primera vez, necesito tener en cuenta el hecho de que cada vez que aparece una vista encima del controlador, se dispara el método viewWillAppear. Esto a su vez generará múltiples copias de la misma notificación. Dado que no hay una manera de comprobar si una notificación ya está activa, obviaré el problema eliminando la notificación antes de agregarla:

- (void)viewWillAppear:(BOOL)animated {
    NSLog(@"viewController will appear");
    // Add observers
    [[NSNotificationCenter defaultCenter] removeObserver:self name:@"imageGenerated" object:nil]; // This is added to avoid duplicate notifications when the view is presented again
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receivedImageFromCameraOrPhotolibraryMethodOnListener:) name:@"actionCompleted" object:nil];

}
 1
Author: Alex,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-22 07:07:32

SWIFT 3

Hay dos casos de uso de notificaciones: - solo se necesitan cuando el controlador de vista está en pantalla; - son necesarios siempre, incluso si el usuario abrió otra pantalla sobre corriente.

Para el primer caso, el lugar correcto para agregar y eliminar el observador es:

/// Add observers
///
/// - Parameter animated: the animation flag
override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    NotificationCenter.default.addObserver(...)
}

/// Remove observers
///
/// - Parameter animated: the animation flag
override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    NotificationCenter.default.removeObserver(self)
}

Para el segundo caso, la forma correcta es:

/// Add observers
override func viewDidLoad() {
    super.viewDidLoad()
    NotificationCenter.default.addObserver(...)
}

/// Remove observers
///
/// - Parameter animated: the animation flag
override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    if self.isBeingDismissed // remove only when view controller is removed disappear forever
    || !(self.navigationController?.viewControllers.contains(self) ?? true) {
        NotificationCenter.default.removeObserver(self)
    }
}

Y nunca poner {[2] {} en[3]} - es un ERROR!

 0
Author: Alexander Volkov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-03 10:06:15
override func viewDidLoad() {   //add observer
  super.viewDidLoad()
  NotificationCenter.default.addObserver(self, selector:#selector(Yourclassname.method), name: NSNotification.Name(rawValue: "NotificationIdentifier"), object: nil)
}

override func viewWillDisappear(_ animated: Bool) {    //remove observer
    super.viewWillDisappear(true)
    NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: "NotificationIdentifier"), object: nil)
}
 0
Author: urvashi bhagat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-31 05:51:02