从NSOperationQueue取消NSOperation会导致崩溃

时间:2012-02-23 09:12:13

标签: iphone ios download nsoperation nsoperationqueue


我正在尝试构建一个下载管理器类,它在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>

有人可以给我一些建议吗? Thanx Andrea

3 个答案:

答案 0 :(得分:4)

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

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

答案 1 :(得分:3)

接受的答案对我有用。只是为了帮助清除这个以防万一其他人遇到它,我也经历了这种崩溃,在异步操作开始执行之前,在isFinished内部- cancel不正确地设置了。{/ p>

我没有这样做,而是将- cancel切换为只更改isFinished如果操作已经isExecuting,那么在- start我立即设置isFinished如此处所示。 Voilà,崩溃了。

答案 2 :(得分:1)

这是一篇快速使用前两个答案的文章:

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()
}