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