NSOperationQueue XXXX isFinished=YES without being started by the queue it is in

来源:互联网 发布:软件免责条款 编辑:程序博客网 时间:2024/06/03 22:04

刚入职公司,负责iOS组的全部工作,同事就给我找了一个难啃的BUG,如题:XXXX isFinished=YES without being started by the queue it is in


问题研究:

1、为何造成Queue里的operation先于start方法执行了 isFinished=YES


a、项目有文件上传下载队列需求

b、对线程执行要求并发

c、设置的最大并发量是2


场景:用户上传3个文件 123

问题出来了:Queue队列里 12正在执行,3正在等待,此时用户暂停正在排队的3,一定出现 XXXX isFinished=YES without being started by the queue it is in 的系统异常信息

此时,用户点击3继续下载,崩溃


2、这个异常operationQueue中到底是个什么存在


这个operationQueue中正在等待,绝对不允许对他的状态做任何改变!!绝对不允许对他的状态做任何改变!!绝对不允许对他的状态做任何改变!!


这里指的是isFinished关联的状态 KVO绝对不能在未执行start方法之前就改动

 

3、正确管理operation的姿势应该是怎样的?



a、一定要重写start方法,why ?



 经我跟小组同事研究得出系统执行start的大致过程:在start里判断当前operation是否已经取消(isCanceled)? 若取消,则不再执行Main方法,并且不会将isFinished置为YES,因为此时isFinished已经被重写 


  因此自定义NSOperation ,如果不重写start方法,就会发生一个情况:我们将3 cancel掉,然后1、2执行完毕,整个Queue就一直卡在3,整个Queue就无效了


2、加一个标志位: _hasStart 

     用于确定是否该给operation及时的设置isFinished的KVO !!!



最后附关键代码:



-(void)start

{  

    _hasStart =YES;


    // Always check for cancellation before launching the task.

    if ([self isCancelled])

    {

        // Must move the operation to the finished state if it is canceled.

        [self willChangeValueForKey:@"isFinished"];

        _operationFinished = YES;

        [self didChangeValueForKey:@"isFinished"];

        

        //内存清空

        [self finishOperation];

        

        return;

    }

    

    // If the operation is not canceled, begin executing the task.

    [self willChangeValueForKey:@"isExecuting"]; 

    [NSThread detachNewThreadSelector:@selector(main)

                             toTarget:self withObject:nil];

    _operationExecuting = YES; 

    [self didChangeValueForKey:@"isExecuting"];

}


- (void)main

{

    NSLog(@"QFOperation main");

    

    // 在异步线程调用就不能够访问主线程的自动释放池

    // 因此在这里新建一个属于当前线程的自动释放池

    @autoreleasepool

    {

        [self startUpload]; // 开始准备上传

    }

}















0 0