从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