从NSOperationQueue取消NSOperation会导致崩溃

我正在尝试构建一个下载管理器类,它在NSOperation子类中包含所有异步下载(每个op都有自己的线程)操作,以便稍后在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  jle 0xa90d9f  0x00a90eae  mov -0x38(%ebp),%ecx 0x00a90eb1  mov -0x34(%ebp),%esi 0x00a90eb4  mov (%esi,%ecx,1),%ecx 0x00a90eb7  mov -0x40(%ebp),%esi 0x00a90eba  cmpb $0x0,(%ecx,%esi,1) 0x00a90ebe  jne 0xa90d9f  0x00a90ec4  mov (%edi,%eax,1),%esi 0x00a90ec7  mov (%esi,%edx,1),%ebx 0x00a90eca  mov %ebx,-0x2c(%ebp) 0x00a90ecd  mov -0x44(%ebp),%ebx 0x00a90ed0  cmpl $0x50,(%esi,%ebx,1) 0x00a90ed4  mov %edi,%ebx 0x00a90ed6  jne 0xa90e96  0x00a90ed8  mov -0x48(%ebp),%ebx 0x00a90edb  cmpb $0x0,(%esi,%ebx,1) 0x00a90edf  mov %edi,%ebx 0x00a90ee1  je 0xa90e96  

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

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

解决方法如下:如果操作在未执行时被取消,则必须在start方法实现后立即将finish var设置为YES,否则如果正在执行则可以将finished设置为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() }