2010-12-29 15 views
27

Wciąż jestem nowy dla bloków w celu-c i zastanawiam się, czy mam ten kod psuedo poprawny. Nie jestem pewien, czy to wystarczy po prostu usunąć obserwatora czy mam zadzwonić removeObserver: Nazwa: obiekt:Poprawne zarządzanie 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: Otrzymuję przerywany EXC_BAD_ACCESS z tego bloku, więc to nie może być dobrze.

Odpowiedz

48

Należy zadeklarować zmienną scanComplete przed zdefiniowaniem samego bloku.

Powodem, dla którego musisz to zrobić, jest to, że próbujesz uzyskać dostęp do zmiennej, która nie istnieje w bloku w momencie definicji, ponieważ sama zmienna nie została jeszcze przypisana.

Co to jest EXC_BAD_ACCESS? Cóż, jest to wyjątek, który jest zgłaszany podczas próby uzyskania dostępu do referencji, która nie istnieje. Tak właśnie jest w twoim przykładzie.

Więc jeśli zadeklarować zmienną przed samym bloku, to powinno działać:

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

Potrzebujesz '__block id scanComplete;', lub zostanie on skopiowany do bloku i będziesz przeciekać obserwatorów. – hwaxxer

+3

W świecie ARC komentarz dotyczący używania '__block' w celu uniknięcia przechwytywania nie jest już prawdziwy. To, co _ jest prawdą, jest takie, że kwalifikator '__block' naprawia podstawowy problem: po zdefiniowaniu bloku' addObserverForName: ...'jeszcze się nie zwróciło, więc przechwycona wartość jest w najlepszym razie' nil' (gdy działa pod ARC, z powodu niejawnego automatycznego braku w deklaracji zmiennej) lub ** niezdefiniowana **, wymienia jedną BAD_ACCESS na całkowicie niezdefiniowane zachowanie , w najgorszym wypadku ... – danyowdee

+0

Usunięcie obserwatora przez odwołanie się do zmiennej lokalnej z wnętrza bloku było zawsze kłujące. Przechowuj zwrócony token obserwatora (tutaj, 'scanComplete') jako zmienną instancji; pod ARC powinna to być zmienna instancji '__weak', aby zapobiec cyklowi zatrzymania na sobie. – matt

-4

zakresem bloku nie ma uprawnień, aby zwolnić obiekt skanera. Jeśli nie używasz zbierania śmieci, usunięcie release i zrobienie autoreakcji skanera ([[[Scanner alloc] init] autorelease]) powinno załatwić sprawę.

Powinieneś być również w stanie bezpiecznie przenieść połączenie do removeObserver poza blok.

W przypadku EXC_BAD_ACCESS: Wprowadzenie do bt w oknie konsoli po zawieszeniu się aplikacji spowoduje zapisanie śledzenia wstecznego i powinno poinformować użytkownika, gdzie wystąpił błąd.

15

Nie należy wyrejestrowywać się w bloku rejestru. Zamiast tego przechowuj token zwrócony z addObserverForName (w tym przypadku, twoja scanComplete) jako zmienną instancji lub w kolekcji, która jest zmienną instancji, i wyrejestruj później, gdy masz zamiar wyjść z istnienia (np. dealloc). To, co robię, to utrzymywanie NSMutableSet o nazwie observers. Więc:

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

A potem:

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

Jeśli chcesz otrzymać jednorazowe powiadomienie, nie rozumiem, dlaczego nie powinieneś być w stanie wyrejestrować się w samym bloku. – ipmcc

3

jabłko dokumentu o tej metodzie:

Poniższy przykład pokazuje w jaki sposób można się zarejestrować, aby otrzymywać powiadomienia o zmianie ustawień narodowych.

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

Aby wyrejestrować obserwacje, należy przekazać obiekt zwracany przez tę metodę, aby usunąćObserwer :. Musisz musi wywołać removeObserver: lub removeObserver: name: object: before dowolny obiekt określony przez addObserverForName: object: queue: usingBlock: is deallocated.

NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; 
[center removeObserver:self.localeChangeObserver]; 
Powiązane problemy