正确管理addObserverForName:object:queue:usingBlock:

sea*_*her 28 cocoa objective-c ios objective-c-blocks

我仍然是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];
}
Run Code Online (Sandbox Code Playgroud)

更新:我EXC_BAD_ACCESS从这个区块收到间歇性,所以这不可能是正确的.

Jac*_*kin 48

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];
}
Run Code Online (Sandbox Code Playgroud)

  • 在ARC世界中,使用`__block`来避免捕获的评论不再适用.什么_does_成立,是`__block`限定符修复了一个基本问题:当定义块时,`addObserverForName:...`还没有返回,所以捕获的值最多是'nil`(运行时)在ARC下,因为它隐含了变量声明的自动nil),或者**undefined**,交换一个BAD_ACCESS用于完全未定义的行为,最坏的情况...... (4认同)
  • @matt为什么?想要完全本地化基于块的通知所涉及的所有移动部件的范围,有什么好看的? (2认同)

mat*_*att 15

您不应该在寄存器块中取消注册.相反,将从addObserverForName(在本例中为您的scanComplete)返回的标记存储为实例变量,或存储在作为实例变量的集合中,并在您即将不存在时(例如,在dealloc)中取消注册.我所做的是保持一个NSMutableSet被调用observers.所以:

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

然后是:

for (id ob in self->observers)
    [[NSNotificationCenter defaultCenter] removeObserver:ob];
self->observers = nil;
Run Code Online (Sandbox Code Playgroud)

  • 如果你想要一次性通知,我不明白为什么你不能在块本身取消注册. (4认同)