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