2010-12-29 11 views
27

estoy todavía nuevo en bloques en Objective-C y se preguntan si tengo esta pseudo código correcto. No estoy seguro de si es suficiente para eliminar sólo el observador o si tengo que llamar removeObserver: Nombre: Objeto:gestión correcta de addObserverForName: Objeto: cola: usingBlock:

-(void) scan { 
    Scanner *scanner = [[Scanner alloc] init]; 
    id scanComplete = [[NSNotificationCenter defaultCenter] addObserverForName:@"ScanComplete" 
         object:scanner 
         queue:nil 
         usingBlock:^(NSNotification *notification){ 
          /* 
          do something 
          */ 
          [[NSNotificationCenter defaultCenter] removeObserver:scanComplete]; 
          [scanner release]; 
         }]; 
    [scanner startScan]; 
} 

Actualización: Estoy recibiendo intermitente EXC_BAD_ACCESS de este bloque, por lo que no puedo estar derecho.

Respuesta

48

declarar la variable scanComplete antes de definir el propio bloque.

La razón por la que tiene que hacer esto se debe a que está tratando de acceder a una variable que no existe dentro del bloque en el momento de la definición ya que la propia variable no se ha asignado todavía.

¿Cuál es EXC_BAD_ACCESS? Bueno, es una excepción que se lanza cuando intenta acceder a una referencia que no existe. Entonces ese es exactamente el caso en tu ejemplo.

Así que si se declara la variable antes de que el propio bloque, entonces debería funcionar:

-(void) scan { 
    Scanner *scanner = [[Scanner alloc] init]; 
    __block id scanComplete; 
    scanComplete = [[NSNotificationCenter defaultCenter] addObserverForName:@"ScanComplete" 
         object:scanner 
         queue:nil 
         usingBlock:^(NSNotification *notification){ 
          /* 
          do something 
          */ 
          [[NSNotificationCenter defaultCenter] removeObserver:scanComplete]; 
          [scanner release]; 
        }]; 
    [scanner startScan]; 
} 
+0

Necesita '__block id scanComplete;', o se copiará en el bloque y tendrá filtraciones de observadores. – hwaxxer

+3

En el mundo de ARC, el comentario sobre el uso de '__block' para evitar la captura ya no es válido. Lo que _desea_ es cierto, es que el calificador '__block' soluciona un problema fundamental: cuando se define el bloque,' addObserverForName: ...'aún no ha regresado, por lo que el valor que se captura es' nil' en el mejor de los casos (cuando se ejecuta en ARC, debido a su declaración de variable auto-nil implícita), o ** indefinido **, intercambiando un BAD_ACCESS por un comportamiento completamente indefinido , en el peor de los casos ... – danyowdee

+0

Quitar al observador haciendo referencia a una variable local desde el interior del bloque siempre fue superficial. Almacene el token de observador devuelto (aquí, 'scanComplete') como una variable de instancia; en ARC esto debería ser una variable de instancia '__weak' para evitar un ciclo de retención en sí mismo. – matt

-4

El ámbito del bloque no tiene permiso para liberar el objeto escáner. Si no está utilizando la recolección de elementos no utilizados, eliminar el release y hacer que el autorrelease se libere automáticamente ([[[Scanner alloc] init] autorelease]) debería ser el truco.

También debe ser capaz de moverse con seguridad la llamada a removeObserver exterior del bloque.

Para el caso de EXC_BAD_ACCESS: Ingresar bt en la ventana de la consola después de que la aplicación falle le dará una traza inversa, y le informará dónde ocurrió el error.

15

No debe anular el registro en el bloque de registro. En su lugar, almacenar el token de regresar de addObserverForName (en este caso, su scanComplete) como una variable de instancia o en una colección que es una variable de instancia, y anular el registro más tarde, cuando estás a punto de dejar de existir (por ejemplo, en dealloc). Lo que hago es mantener un NSMutableSet llamado observers. Por lo tanto:

id ob = [[NSNotificationCenter defaultCenter] 
    addObserverForName:@"whatever" object:nil queue:nil 
    usingBlock:^(NSNotification *note) { 
     // ... whatever ... 
}]; 
[self->observers addObject:ob]; 

Y más tarde:

for (id ob in self->observers) 
    [[NSNotificationCenter defaultCenter] removeObserver:ob]; 
self->observers = nil; 
+4

Si desea una notificación de una sola vez, no veo por qué no debería poder anular el registro en el bloque. – ipmcc

3

de Apple documento acerca de este método:

El siguiente ejemplo muestra cómo se puede registrar para recibir notificaciones de cambio de la configuración regional.

NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; 
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue]; 
self.localeChangeObserver = [center addObserverForName:NSCurrentLocaleDidChangeNotification object:nil 
    queue:mainQueue usingBlock:^(NSNotification *note) { 

     NSLog(@"The user's locale changed to: %@", [[NSLocale currentLocale] localeIdentifier]); 
    }]; 

Para anular el registro de las observaciones, pase el objeto devuelto por este método para eliminar Observer :. Usted debe invoke removeObserver: o removeObserver: nombre: objeto: antes de cualquier objeto especificado por addObserverForName: object: queue: usingBlock: es desasignado.

NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; 
[center removeObserver:self.localeChangeObserver]; 
Cuestiones relacionadas