【问题标题】:Cancelling NSOperation from NSOperationQueue cause crash从 NSOperationQueue 取消 NSOperation 导致崩溃
【发布时间】:2012-02-23 09:12:12
【问题描述】:


我正在尝试构建一个下载管理器类,它将所有异步下载(每个操作都有自己的线程)操作打包到 NSOperation 子类中,以便稍后将它们添加到 NSOperationQueue 中。下载管理器类(单例)还公开了一些方法来处理队列并取消符合某些要求的操作。
这些是开始创建类集群(抽象工厂)的步骤,它为不同类型的常见操作(上传、下载、解析等)返回不同类型的 NSOperation。
该类似乎可以很好地处理下载操作,但如果在这些操作的中间我调用一个取消操作的方法,该操作会成功取消,但应用程序稍后会崩溃几个操作。如果我不取消任何操作,一切正常。使用 KVO 观察所有操作。 删除操作的方法如下:

- (void) cancelDownloadOperationWithID:(NSString *)aUUID{
@synchronized(self){
    [self.dowloadQueue setSuspended:YES]; //downloadQueue is an NSOperationQueue
    NSArray * downloadOperations = [self.dowloadQueue operations];
    NSPredicate * aPredicate = [NSPredicate predicateWithFormat:@"SELF.connectionID == %@",aUUID]; //SELF is the signleton instance of the download manager
    NSArray * filteredArray = [downloadOperations filteredArrayUsingPredicate:aPredicate];
    if ([filteredArray count]==0) {
        [self.dowloadQueue setSuspended:NO];
        return;
    } 
    [filteredArray makeObjectsPerformSelector:@selector(cancel)];
    NSLog(@"Cancelled %d operations",[filteredArray count]);
    [self.dowloadQueue setSuspended:NO];
   }
}

崩溃日志非常难以理解,但它是 BAD_EXC_ACCESS(可能是僵尸),请注意我在 ARC 下。

0x00a90ea8  <+0393>  jle    0xa90d9f <____NSOQSchedule_block_invoke_0+128>
0x00a90eae  <+0399>  mov    -0x38(%ebp),%ecx
0x00a90eb1  <+0402>  mov    -0x34(%ebp),%esi
0x00a90eb4  <+0405>  mov    (%esi,%ecx,1),%ecx
0x00a90eb7  <+0408>  mov    -0x40(%ebp),%esi
0x00a90eba  <+0411>  cmpb   $0x0,(%ecx,%esi,1)
0x00a90ebe  <+0415>  jne    0xa90d9f <____NSOQSchedule_block_invoke_0+128>
0x00a90ec4  <+0421>  mov    (%edi,%eax,1),%esi
0x00a90ec7  <+0424>  mov    (%esi,%edx,1),%ebx
0x00a90eca  <+0427>  mov    %ebx,-0x2c(%ebp)
0x00a90ecd  <+0430>  mov    -0x44(%ebp),%ebx
0x00a90ed0  <+0433>  cmpl   $0x50,(%esi,%ebx,1)
0x00a90ed4  <+0437>  mov    %edi,%ebx
0x00a90ed6  <+0439>  jne    0xa90e96 <____NSOQSchedule_block_invoke_0+375>
0x00a90ed8  <+0441>  mov    -0x48(%ebp),%ebx
0x00a90edb  <+0444>  cmpb   $0x0,(%esi,%ebx,1)
0x00a90edf  <+0448>  mov    %edi,%ebx
0x00a90ee1  <+0450>  je     0xa90e96 <____NSOQSchedule_block_invoke_0+375>

有人可以给我建议吗?
谢谢安德里亚

【问题讨论】:

    标签: iphone ios download nsoperation nsoperationqueue


    【解决方案1】:

    答案很简单。在 NSOperation 子类的覆盖 -cancel 方法中,我设置了已完成和正在执行的变量,以触发正确的 KVO 回调。问题是,即使取消操作,操作也会保留在 NSOperationQueue 中,当队列尝试在触发其 KVO 回调的 NSOperationQueue 上启动 -start 方法时,它会崩溃。

    解决方法如下:如果操作在未执行时被取消,则必须在start方法执行后立即将finish var设置为YES,否则如果正在执行,则可以将finish设置为YES并执行到NO。

    【讨论】:

    • 这对我来说似乎很奇怪,为什么队列会保留尚未启动/排队的操作?谢谢,它解决了我的崩溃问题。
    • 对我来说也是如此,但这是有道理的,我们没有出队,我们只是取消了一个操作。
    【解决方案2】:

    接受的答案对我有用。为了帮助解决这个问题,以防其他人遇到它,我还通过在我的 - cancel 中不正确地设置 isFinished 在异步操作开始执行之前遇到了这个崩溃。

    我没有这样做,而是将我的- cancel 切换为仅更改isFinished 如果操作已经是isExecuting,然后在- start 中我立即按照此处的建议设置isFinished。瞧,崩溃消失了。

    【讨论】:

      【解决方案3】:

      这是使用前两个答案的 swift 片段:

      override func cancel() {
          super.cancel()
      
          if executing {
              executing = false
              finished = true
          }
      
          task.cancel()
      }
      
      override func start() {
          if cancelled {
              finished = true
              return
          }
      
          executing = true
      
          main()
      }
      
      override func main() {
          task.resume()
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-10-30
        • 1970-01-01
        • 2016-12-14
        • 2021-12-11
        • 2014-04-28
        相关资源
        最近更新 更多