【问题标题】:NSOperation KVO problemNSOperation KVO 问题
【发布时间】:2010-07-08 10:02:46
【问题描述】:

我正在使用以下函数在 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];

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

真的可以吗?

提前感谢。

【问题讨论】:

    标签: iphone nsoperation nsoperationqueue


    【解决方案1】:

    如果您需要 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 支持不同队列上的操作之间的依赖关系。请注意不要使不同队列上的操作相互依赖,因为这会导致死锁。

    【讨论】:

      【解决方案2】:

      您看到此问题的原因是您没有使用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: 中的 super,因此该通知进入但从未发出。这就是你得到这个异常的原因。

      很遗憾,IntarWebs 上提供的许多键值观察示例没有正确执行此操作和/或将 NULL 作为上下文指针传递(甚至 Apple 的文档也这样做)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-08-08
        • 2011-09-27
        • 1970-01-01
        • 2011-01-18
        • 1970-01-01
        相关资源
        最近更新 更多