Правильное управление addObserverForName: object: queue: usingBlock: - PullRequest
28 голосов
/ 29 декабря 2010

Я все еще новичок в блоках в target-c и задаюсь вопросом, правильный ли у меня этот код псевдо.Я не уверен, достаточно ли просто удалить наблюдателя или мне нужно вызвать removeObserver: name: object:

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

Обновление: я получаю прерывистый EXC_BAD_ACCESS из этого блока, так что этоне может быть правдой.

Ответы [ 4 ]

48 голосов
/ 29 декабря 2010

Объявите переменную scanComplete перед определением самого блока.

Причина, по которой вам нужно это сделать, заключается в том, что вы пытаетесь получить доступ к переменной, которая не существует в блоке в данный моментопределения, поскольку сама переменная еще не была назначена.

Что такое EXC_BAD_ACCESS?Ну, это исключение, которое выдается, когда вы пытаетесь получить доступ к несуществующей ссылке.Так что это именно тот случай в вашем примере.

Так что если вы объявляете переменную перед самим блоком, то она должна работать:

-(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];
}
15 голосов
/ 23 ноября 2011

Вы не должны отменять регистрацию в регистре.Вместо этого сохраните токен, возвращенный из addObserverForName (в данном случае, scanComplete), в качестве переменной экземпляра или в коллекции, которая является переменной экземпляра, и отмените регистрацию позже, когда вы собираетесь прекратить существование (например, вdealloc).Что я делаю, так это сохраняю NSMutableSet с именем observers.Итак:

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

А потом:

for (id ob in self->observers)
    [[NSNotificationCenter defaultCenter] removeObserver:ob];
self->observers = nil;
3 голосов
/ 01 июня 2015

Apple, документ об этом методе:

В следующем примере показано, как зарегистрироваться, чтобы получать уведомления об изменении локали.

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

Чтобы отменить регистрацию наблюдений, вы передаете объект, возвращенный этим методом, в removeObserver :. Вы должны вызвать removeObserver: или removeObserver: имя: объект: перед тем, как какой-либо объект, указанный в addObserverForName: объект: очередь: usingBlock: будет освобожден.

NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center removeObserver:self.localeChangeObserver];
0 голосов
/ 29 декабря 2010

Область действия блока не имеет разрешения на освобождение объекта сканера.Если вы не используете сборщик мусора, удаление release и выполнение автоматического выпуска сканера ([[[Scanner alloc] init] autorelease]) должны помочь.

Вы также можете безопасно перевести вызов на removeObserver за пределыблока.

Для случая EXC_BAD_ACCESS: Ввод bt в окне консоли после сбоя приложения вернет вас назад и сообщит, где произошла ошибка.

...