从NSOperationQueue中取消NSOperation会导致崩溃


我试图构建一个下载管理器类,将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


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

解决方法如下:如果操作在执行过程中被取消,则必须在启动方法实现后立即将完成var设置为YES,否则如果正在执行,则可以将完成设置为YES并执行至NO。


接受的答案适用于我。 只是为了帮助解决这个问题,以防其他人遇到它时,我也会在异步操作开始执行之前,通过在我的- cancel设置isFinished经历这次崩溃。

而不是这样做,我切换我的- cancel只改变isFinished如果该操作已经isExecuting ,然后在- start我立即按照建议这里isFinished 。 Voilà,崩溃消失了。


这里是一个快速使用两个以前的答案:

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()
}
链接地址: http://www.djcxy.com/p/65117.html

上一篇: Cancelling NSOperation from NSOperationQueue cause crash

下一篇: NSThread vs. NSOperationQueue vs. ??? on the iPhone