2010-12-29 15 views
27

Sono ancora nuovo per i blocchi in ogg-c e mi chiedo se ho questo codice psuedo corretto. Non sono sicuro se è abbastanza per rimuovere solo l'osservatore o se devo chiamare removeObserver: Nome: oggetto:Gestione corretta di addObserverForName: object: queue: 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]; 
} 

Update: Sto ricevendo intermittente EXC_BAD_ACCESS da questo blocco, in modo da questo non può essere destra.

risposta

48

Dichiarare la variabile scanComplete prima di definire il blocco stesso.

Il motivo per cui è necessario farlo è perché si sta tentando di accedere a una variabile che non esiste all'interno del blocco al momento della definizione poiché la variabile stessa non è ancora stata assegnata.

Che cos'è EXC_BAD_ACCESS? Bene, si tratta di un'eccezione generata quando si tenta di accedere a un riferimento che non esiste. Questo è esattamente il caso nel tuo esempio.

Quindi, se si dichiara la variabile prima del blocco stesso, quindi dovrebbe funzionare:

-(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

È necessario '__block id scanComplete;', o verrà copiato nel blocco e si verificheranno gli osservatori. – hwaxxer

+3

Nel mondo di ARC, il commento sull'uso di '__block' per evitare l'acquisizione non è più valido. Ciò che _viene_ è vero, è che il qualificatore '__block' corregge un problema fondamentale: quando il blocco è definito,' addObserverForName: ...'non è ancora stato restituito, quindi il valore che viene catturato è' nil' nella migliore delle ipotesi (quando è in esecuzione in ARC, a causa del suo auto-nil implicito sulla dichiarazione delle variabili), o ** non definito **, scambiando un BAD_ACCESS per un comportamento completamente indefinito , nel peggiore dei casi ... – danyowdee

+0

Rimozione dell'osservatore facendo riferimento a una variabile locale dall'interno del blocco era sempre scialbo. Memorizza il token dell'osservatore restituito (qui, 'scanComplete') come variabile di istanza; sotto ARC questa dovrebbe essere una variabile di istanza '__weak' per impedire un ciclo di mantenimento su se stesso. – matt

-4

Il campo di applicazione del blocco non ha il permesso di rilasciare l'oggetto dello scanner. Se non si sta utilizzando la garbage collection, rimuovere lo release e rendere la scansione automatica dello scanner ([[[Scanner alloc] init] autorelease]) dovrebbe fare il trucco.

Dovresti anche essere in grado di spostare la chiamata in modo sicuro a removeObserver al di fuori del blocco.

Per il caso di EXC_BAD_ACCESS: l'immissione di bt nella finestra della console dopo che l'applicazione si è arrestata in modo anomalo darà un backtrace e dovrebbe informare dove si è verificato l'errore.

15

Non è necessario annullare la registrazione nel blocco di registro. Invece, memorizza il token restituito da addObserverForName (in questo caso, il tuo) come variabile di istanza o in una raccolta che è una variabile di istanza e annullare la registrazione più tardi quando stai per esaurire l'esistenza (ad esempio in dealloc). Quello che faccio è mantenere un NSMutableSet chiamato observers. Quindi:

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

E poi:

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

Se si desidera una notifica one-shot, non vedo perché non si dovrebbe poter annullare la registrazione nel blocco stesso. – ipmcc

3

di Apple documento su questo metodo:

L'esempio seguente mostra come è possibile registrarsi per ricevere le notifiche di modifica delle impostazioni internazionali.

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]); 
    }]; 

Per annullare la registrazione delle osservazioni, si passa l'oggetto restituito da questo metodo a removeObserver :. deve invocare removeObserver: o removeObserver: name: object: prima di qualsiasi oggetto specificato da addObserverForName: object: queue: usingBlock: deallocated.

Problemi correlati