29

我对objective-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

4 回答 4

49

在定义块本身之前声明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];
}
于 2010-12-29T04:16:24.710 回答
15

您不应该在注册块中取消注册。相反,将从addObserverForName(在本例中为您的scanComplete)返回的令牌存储为实例变量或作为实例变量的集合中,并在您即将不存在时(例如在dealloc)中注销。我所做的是保留一个名为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;
于 2011-11-23T18:56:11.680 回答
3

关于此方法的 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:。您必须在 addObserverForName:object:queue:usingBlock: 指定的任何对象被释放之前调用 removeObserver: 或 removeObserver:name:object:。

NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center removeObserver:self.localeChangeObserver];
于 2015-06-01T02:10:03.820 回答
-4

该块的范围没有释放扫描仪对象的权限。如果您不使用垃圾收集,删除release并让扫描仪自动释放 ( [[[Scanner alloc] init] autorelease]) 应该可以解决问题。

您还应该能够安全地将调用移到removeObserver块之外。

对于这种情况EXC_BAD_ACCESSbt在应用程序崩溃后进入控制台窗口会给你一个回溯,并且应该告诉你错误发生在哪里。

于 2010-12-29T09:21:18.133 回答