Правильное управление 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 ответа:
Объявите переменную
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
в окно консоли после сбоя приложения даст вам обратный путь и должен сообщить вам, где произошла ошибка.