设置isFinished时取消NSOperation EXC_BAD_ACCESS崩溃

时间:2016-01-27 12:14:01

标签: ios objective-c exc-bad-access key-value-observing nsoperation

我有一个NSOperation子类,它从UITableView运行异步操作。

我覆盖了正确的开始和结束方法,如下所示:

- (void)start
{
    [self willChangeValueForKey:@"isExecuting"];
    self.isExecuting = YES;
    [self didChangeValueForKey:@"isExecuting"];

    if (self.isCancelled)
    {
        [self finish];
        return;
    }
}
- (void)finish
{
       if (!_isExecuting)
        {
            [self willChangeValueForKey:@"isExecuting"];
            _isExecuting = YES;
            [self didChangeValueForKey:@"isExecuting"];
        }

        [self willChangeValueForKey:@"isExecuting"];
        [self willChangeValueForKey:@"isFinished"];

        _isExecuting = NO;
        _isFinished = YES;

        [self didChangeValueForKey:@"isExecuting"];
        [self didChangeValueForKey:@"isFinished"];
}

我遇到的问题,如果我向下滚动表并删除一行,则会在操作中调用cancel方法,但随着操作逐渐完成并且它进一步向下移动,它会崩溃第EXC_BAD_ACCESS

上出现[self didChangeValueForKey:@"isFinished"];错误

将代码粘贴到此处的代码非常复杂,但我想知道的是如何跟踪导致KVO消息崩溃的对象?

如果我在调试器中启用了僵尸对象,它根本不会崩溃而没有任何不起作用的警告。

如果我在try/catch中包装KVO方法,它永远不会被抓住并且仍会崩溃。

我尝试在我的NSOperation子类中覆盖KVO方法,但它们永远不会被调用:

- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context
{
    NSLog(@"%s - %@", __PRETTY_FUNCTION__, observer);
    [super addObserver:observer forKeyPath:keyPath options:options context:context];
}

是否可以看到观察者是谁?

2 个答案:

答案 0 :(得分:2)

评论和想法:

  1. start self.isExecuting = YES不应该_isExecuting = YES;
  2. 添加一个名为identifier的NSString类型的属性,并为每个操作设置它。
  3. 添加dealloc方法并记录identifier
  4. finish的{​​{1}}测试中,如果是,请立即返回。
  5. 另一个想法是双重保留操作 - 将它们放在以isCancelled为键的NSDictionary中,并查看是否有任何更改。

答案 1 :(得分:1)

我遇到了同样的问题。 发生这种情况是因为您在调用start方法之前为isFinished变量设置了值。

要解决此问题,您需要在取消方法中设置标志isCancelled = YES(且仅)。 然后在-start方法中检查取消状态。

如果操作在开始之前被取消,那么您应该已经设置了这些值:

    self.executing = YES;
    self.executing = NO;
    self.finished  = YES;

所有代码:

-(void)start
{
   if ((self.ready == YES) && (self.executing == NO) && ((self.finished == NO)) // if Ready To Start State
  {
    self.ready = NO; self.executing = YES; self.finished = NO; // Setting  Working State

    [self receiveItemAtURL:self.url params:self.params
           timeStoreExpire:self.storageTime
                  progress:self.operationProgressBlock
                completion:self.operationCompletionBlock];

  }else if ((self.ready == NO) && (self.cancelled == YES){ // If Cancelled State
    self.executing = YES;
    self.executing = NO;
    self.finished  = YES;
   }
}


- (void) cancel
 {
    if ((self.ready == YES) && (self.executing == NO) && ((self.finished == NO))      { // If Working State
    self.ready = NO; self.cancelled = YES; // Setting Cancelled State
     }
    else if ((self.ready == NO) && (self.executing == YES) && (self.finished == NO) || // If Working State
         (self.ready == YES) && (self.executing == NO) && (self.finished == NO))   // If Suspend State
   {
    self.state = RXCOCancelled;
    self.executing = YES;
    self.executing = NO;
    self.finished  = YES;
   }
 }