objective c - Objetivo C: ¿Dónde eliminar al observador para NSNotification?
notificationcenter swift 4 (14)
Tengo una clase C objetiva. En él, creé un método init y configuré una NSNotificación en él
//Set up NSNotification
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(getData)
name:@"Answer Submitted"
object:nil];
¿Dónde configuro el [[NSNotificationCenter defaultCenter] removeObserver:self]
en esta clase? Sé que para un UIViewController
, puedo agregarlo al método viewDidUnload
Entonces, ¿qué hay que hacer si acabo de crear un objetivo c Class?
Nota: Esto ha sido probado y funciona al 100%
Rápido
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)
}
}
C objetivo
En iOS 6.0 > version
, es mejor eliminar el observador en viewWillDisappear
ya que el método viewDidUnload
está en desuso.
[[NSNotificationCenter defaultCenter] removeObserver:observerObjectHere];
Hay muchas veces que es mejor 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];
}
}
¡Creo que encontré una respuesta confiable ! Tuve que hacerlo, ya que las respuestas anteriores son ambiguas y parecen contradictorias. Miré a través de los libros de cocina y las 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 secundaria para ejecutar código en el controlador de vista padre. Solo usaría este estilo si estuviera publicando y escuchando la notificación dentro del mismo controlador de vista.
La respuesta en la que más confiaré, la encontré en la Programación de iOS: Big Nerd Ranch Guide 4th. Confío en los chicos de BNR porque tienen centros de capacitación de iOS y no solo están escribiendo otro libro de cocina. Probablemente sea mejor para ellos ser precisos.
Primer ejemplo de BNR: addObserver:
en init:
removeObserver:
en dealloc:
Ejemplo 2 de BNR: addObserver:
en awakeFromNib:
removeObserver:
en dealloc:
... al eliminar el observador en dealloc:
no usan [super dealloc];
Espero que esto ayude a la próxima persona ...
Estoy actualizando esta publicación porque Apple ahora se 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é esta publicación en primer lugar) es prestar atención si su vista viewWillDisappear:
se está llamando. No fue para mí cuando la aplicación entró en segundo plano.
* editar: este consejo se aplica a iOS <= 5 (incluso allí debe agregar viewWillAppear
y eliminar en viewWillDisappear
; sin embargo, el consejo se aplica si por algún motivo ha agregado el observador en viewDidLoad
)
Si ha agregado el observador en viewDidLoad
, debe eliminarlo en dealloc
y viewDidUnload
. De lo contrario, terminará agregándolo dos veces cuando se viewDidLoad
viewDidUnload
después de viewDidUnload
(esto sucederá después de una advertencia de memoria). Esto no es necesario en iOS 6 donde viewDidUnload
está en desuso y no se llamará (porque las vistas ya no se descargan automáticamente).
Desde iOS 9 ya no es necesario eliminar observadores.
En OS X 10.11 y iOS 9.0, NSNotificationCenter y NSDistributedNotificationCenter ya no enviarán notificaciones a los observadores registrados que puedan ser desasignados.
En general, lo puse en el método dealloc
.
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 está obsoleto.
En resumen, siempre lo hago en viewDidDisappear
. Sin embargo, también depende de sus requisitos, al igual que @Dirk dijo.
En uso rápido deinit porque dealloc no está disponible:
deinit {
...
}
Documentación de Swift:
Se llama a un desinicializador inmediatamente antes de desasignar una instancia de clase. Usted escribe desinicializadores con la palabra clave deinit, similar a cómo se escriben los intializadores con la palabra clave init. Los desinicializadores solo están disponibles en los tipos de clase.
Por lo general, no es necesario realizar una limpieza manual cuando se desasignan sus instancias. Sin embargo, cuando trabaje con sus propios recursos, es posible que necesite realizar una limpieza adicional usted mismo. Por ejemplo, si crea una clase personalizada para abrir un archivo y escribirle algunos datos, es posible que deba cerrar el archivo antes de desasignar la instancia de la clase.
Es importante observar también que se llama a viewWillDisappear
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, generalmente elimino 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];
}
Por razones similares, cuando publico la notificación la primera vez, necesito explicar el hecho de que cada vez que aparece una vista con encima del controlador, se viewWillAppear
método viewWillAppear
. Esto a su vez generará una copia múltiple de la misma notificación. Como no hay forma de verificar si una notificación ya está activa, elimino la notificación antes de agregarla: obvio el problema.
- (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];
}
La respuesta aceptada no es segura y podría causar una pérdida de memoria. Por favor, deje el registro en dealloc, pero también elimine el registro en viewWillDisappear (eso es, por supuesto, si se registra en viewWillAppear) ... ¡ESO ES LO QUE HICE DE TODOS MODOS Y FUNCIONA EXCELENTE! :)
La respuesta genérica sería "tan pronto como ya no necesite las notificaciones". Esto obviamente 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 utilizar como observadores, ya que es la última oportunidad para anular el registro de un observador limpiamente. Sin embargo, esto solo lo protegerá contra fallas debido a que el centro de notificaciones notificará objetos muertos. No puede proteger su código contra la recepción de notificaciones, cuando sus objetos aún no están / ya no se encuentran en un estado en el que puedan manejar adecuadamente la notificación. Para esto ... Ver arriba.
Editar (ya que la respuesta parece atraer más comentarios de los que habría pensado) Todo lo que trato 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 de:
- En su caso de uso (¿Qué notificaciones se cumplen? ¿Cuándo se envían?)
- La implementación del observador (¿Cuándo está listo para recibir notificaciones? ¿Cuándo ya no está listo?)
- La duración prevista del observador (¿está vinculada a algún otro objeto, por ejemplo, un controlador de vista o vista?)
- ...
Entonces, el mejor consejo general que se me ocurre es: proteger tu aplicación. contra al menos una posible falla, haga removeObserver:
dance in dealloc
, ya que ese es el último punto (en la vida del objeto), donde puede hacerlo limpiamente. Lo que esto no significa es: "simplemente dealloc
la eliminación hasta que se dealloc
, y todo estará bien". En su lugar, elimine al observador tan pronto como el objeto ya no esté listo (o requerido) para recibir notificaciones . Ese es el momento exacto y correcto. Lamentablemente, 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 removeObserver:
forma segura removeObserver:
un objeto varias veces (y todas menos la primera llamada con un observador determinado serán nops). Entonces: piense en hacerlo (nuevamente) en dealloc
solo para estar seguro, pero antes que nada: hágalo en el momento apropiado (que está determinado por su caso de uso).
Si el observador se agrega a un controlador de vista , recomiendo agregarlo en viewWillAppear
y eliminarlo en viewWillDisappear
.
SWIFT 3
Hay dos casos de uso de notificaciones: - se necesitan solo cuando el controlador de visualización está en pantalla; - se necesitan siempre, incluso si el usuario abrió otra pantalla sobre la actual.
Para el primer caso, el lugar correcto para agregar y eliminar al observador son:
/// 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 ponga removeObserver
in deinit{ ... }
- ¡es un ERROR!
-(void) dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}
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)
}