NSOperation KVO问题

时间:2010-07-08 10:02:46

标签: iphone nsoperation nsoperationqueue

我正在使用以下函数在nsoperationqueue中的操作完成后通知我的应用程序,以便我可以安排依赖于操作结果的任务。我正在使用:

- (void)observeValueForKeyPath:(NSString *)keyPath 
                      ofObject:(id)object 
                        change:(NSDictionary *)change 
                       context:(void *)context
{
  if([keyPath isEqual:@"isFinished"] && _operation == object)
  {  
    NSLog(@"Our Thread Finished!");
    [_operation removeObserver:self forKeyPath:@"isFinished"];
    [self performSelectorOnMainThread:@selector(showDialog) withObject:nil waitUntilDone:YES];
  } 
}

我的问题是,由于分配给这些操作的任务主要是解析数据,如果我尝试点击其他按钮或基本上做了导致操作的事情,我会得到以下异常:

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '<Settings: 0x21b970>: An -observeValueForKeyPath:ofObject:change:context: message was received but not handled.
Key path: isFinished

我完全理解,因为我尝试在主线程上做其他事情,因为调用主线程:

[self performSelectorOnMainThread:@selector(showDialog) withObject:nil waitUntilDone:YES];

无法执行。但是这个问题的解决方案是什么,因为我希望两者都允许用户在发出请求后执行任何操作,并在完成分配给操作的任务后执行计划的操作。

真的有可能吗?

提前完成。

2 个答案:

答案 0 :(得分:0)

如果您需要Mac OS X 10.6 Snow Leopard或(我认为)iPhone OS 3.0,您可以完全避免使用KVO。只需为要在主线程上完成的工作创建另一个操作,添加它需要作为依赖项执行的操作,然后将主线程操作放在主队列上:

NSBlockOperration *mainThreadOp = [NSBlockOperation blockOperationWithBlock:^{
    [self showDialog];
}];
[mainThreadOp addDependency:backgroundOp];
[[NSOperationQueue mainQueue] addOperation:mainThreadOp];

NSOperation支持不同队列上的操作之间的依赖关系。注意不要让不同队列上的操作相互依赖,因为这会导致死锁。

答案 1 :(得分:0)

您遇到此问题的原因是您没有使用context指针。 添加或删除观察者时,传递上下文指针。在observeValueForKeyPath:ofObject:change:context:中,检查上下文点以确保传递的观察属于您。如果没有,请致电超级。可以使用self作为上下文点,或者可以使用静态字符串的地址等。 这是一个例子:

添加观察者:

[sample addObserver:self forKeyPath:@"finished" options:[self observationOptions] context:(void *)self];

处理变更通知:

- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if (context == (__bridge void *)self){
        if ([keyPath isEqualToString:@"finished"]){
            // Handle the change here.
        }
    } else {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}

删除观察者:

[sample removeObserver:self forKeyPath:@"finished" context:(void *)self];

由于您的代码无法将其中一个通知传递给observeValueForKeyPath:ofObject:change:context:中的超级通知,因此该通知已进入,但从未发布过。这就是你遇到这个例外的原因。

很遗憾,IntarWebs上提供的许多Key-Value Observing示例都没有正确执行此操作和/或将NULL作为上下文指针传递(即使是Apple的文档也这样做)。