2010-12-29 4 views
27

Ich bin noch neu zu Blöcken in objective-c und frage mich, ob ich diesen Pseudo-Code richtig habe. Ich bin mir nicht sicher, ob es genug ist, um nur den Beobachter zu entfernen oder wenn ich removeObserver müssen nennen: Name: Objekt:Korrekte Verwaltung von addObserverForName: Objekt: Warteschlange: 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: Ich intermittierende EXC_BAD_ACCESS von diesem Block bin empfangen, so kann dies nicht sein Recht.

Antwort

48

Deklarieren Sie die Variable scanComplete, bevor Sie den Block selbst definieren.

Der Grund, warum Sie dies tun müssen, ist, weil Sie versuchen, auf eine Variable zuzugreifen, die zum Zeitpunkt der Definition nicht innerhalb des Blocks existiert, da die Variable selbst noch nicht zugewiesen wurde.

Was ist EXC_BAD_ACCESS? Nun, es ist eine Ausnahme, die ausgelöst wird, wenn Sie versuchen, auf eine Referenz zuzugreifen, die nicht existiert. Das ist in Ihrem Beispiel genau der Fall.

Also, wenn Sie die Variable vor dem Block selbst erklären, dann sollte es funktionieren:

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

Sie brauchen '__block id scanComplete;', oder es wird in den Block kopiert und Sie werden Beobachter leckt. – hwaxxer

+3

In der Welt von ARC gilt der Kommentar zur Verwendung von '__block' zur Vermeidung von Capture nicht mehr. Was _does_ stimmt, ist, dass der '__block' Qualifikationsmerkmal ein fundamentales Problem behebt: wenn der Block definiert ist,' addObserverForName: ...'ist noch nicht zurückgekehrt, daher ist der Wert, der erfasst wird, bestenfalls 'nil' (wenn er unter ARC wegen seiner impliziten automatischen Nullpunkt-Variablendeklaration ausgeführt wird) oder ** undefined ** und tauscht einen BAD_ACCESS für völlig undefiniertes Verhalten , im schlimmsten Fall ... – danyowdee

+0

Das Entfernen des Beobachters durch Bezugnahme auf eine lokale Variable innerhalb des Blocks war immer skanky. Speichern Sie das zurückgegebene Beobachter-Token (hier "scanComplete") als Instanzvariable; Unter ARC sollte dies eine '__weak'-Instanzvariable sein, um einen Retain-Zyklus auf self zu verhindern. – matt

-4

Der Umfang des Blocks nicht die Erlaubnis, das Scanner-Objekt freigeben muss. Wenn Sie keine Garbage-Collection verwenden, sollten Sie den release entfernen und den Scanner automatisch freigeben ([[[Scanner alloc] init] autorelease]).

Sie sollten auch in der Lage sein, den Anruf sicher zu removeObserver außerhalb des Blocks zu verschieben.

Für den Fall EXC_BAD_ACCESS: Eingabe von bt in das Konsolenfenster nach dem Absturz der Anwendung erhalten Sie eine Rückverfolgung, und sollten Sie informieren, wo der Fehler aufgetreten ist.

15

Sie sollten die Registrierung im Registerblock nicht aufheben. Speichern Sie stattdessen das Token, das von addObserverForName (in diesem Fall Ihre scanComplete) zurückgegeben wird, als eine Instanzvariable oder in einer Auflistung, die eine Instanzvariable ist, und die Registrierung später wenn Sie nicht mehr existieren (z. B. in dealloc). Was ich mache, ist ein NSMutableSet namens observers. Also:

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

Und dann später:

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

Wenn Sie eine einmalige Benachrichtigung wünschen, sehe ich nicht, warum Sie nicht in der Lage sein sollten, die Registrierung im Block selbst aufzuheben. – ipmcc

3

Apple-Dokument über diese Methode:

Das folgende Beispiel zeigt, wie Sie locale Änderungsbenachrichtigungen erhalten registrieren.

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

Beobachtungen deregistrieren, können Sie das Objekt übergeben von dieser Methode zurück removeObserver :. Sie müssen aufrufen removeObserver: oder removeObserver: name: object: vor einem beliebigen Objekt von addObserverForName angegeben: object: queue: usingBlock: wird freigegeben.

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