刚入职公司,负责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个文件 1、2、3
问题出来了:Queue队列里 1、2正在执行,3正在等待,此时用户暂停正在排队的3,一定出现 XXXX isFinished=YES without being started by the queue it is in 的系统异常信息
此时,用户点击3继续下载,崩溃
2、这个异常operation在Queue中到底是个什么存在 ?
这个operation在Queue中正在等待,绝对不允许对他的状态做任何改变!!绝对不允许对他的状态做任何改变!!绝对不允许对他的状态做任何改变!!
这里指的是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]; // 开始准备上传
}
}