Правильное управление addObserverForName: object: queue: usingBlock:


Я все еще новичок в блоках objective-c и задаюсь вопросом, правильно ли у меня этот код psuedo. Я не уверен, достаточно ли просто удалить наблюдателя или мне нужно вызвать 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 27

4 ответа:

Объявите переменную 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];
}

Вы не должны отменять регистрацию в блоке register. Вместо этого сохраните токен, возвращенный из 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;

Документ 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: name: object: перед любым объектом, указанным addObserverForName:object:queue:usingBlock: освобождается.

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

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

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

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