2012-02-23 77 views
2


我试图建立一个下载管理器类,在NSOperationQueue稍后将它们添加到一个NSOperation子类中的所有异步下载(每个OP有它自己的线程)操作。下载管理器类(单例)也暴露了几个方法来处理符合某些要求的队列和取消操作。
这些是开始创建一种类群(抽象工厂)的步骤,它为不同类型的常见操作(上传,下载,解析等)返回不同类型的NSOperation。
该类似乎在下载操作中运行得非常好,但如果在这些操作的中间我调用取消操作的方法,操作将被成功取消,但应用程序稍后会崩溃。如果我不取消任何操作,一切正常。所有操作都使用KVO进行观察。 该删除操作看起来像这样的方法:从NSOperationQueue取消NSOperation导致崩溃

- (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

回答

4

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

变通方法如下:如果在它不执行操作被取消,则必须将启动方法执行后立即设置完成VAR为YES,否则,如果它正在执行它的确定设置完成到YES,执行到NO。

+0

这似乎是很奇怪的行为对我来说,为什么会排队保持这一尚未开始运营/排队?谢谢,它修复了我的崩溃。 – 2013-11-12 12:25:46

+0

对我来说也是如此,但它是有道理的,我们不是出队,我们只是取消一个操作。 – Andrea 2013-11-12 13:40:30

3

接受的答案适用于我。只是为了帮助解决这个问题,以防其他人遇到它,在异步操作开始执行之前,我还通过在我的- cancel中错误地设置isFinished来发生此故障。

不是这样做,我打开我的- cancel只改变isFinished操作是否已经isExecuting,然后在- start我立即成立isFinished如下建议。 Voilà,崩溃消失了。

+1

你可以发布一些代码...谢谢 – user1028028 2014-07-09 10:50:50

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