NSOperation和NSOperationQueue验证笔记

  • 如果想了解NSOperation和NSOperationQueue的基本概念或者一些理解,请先阅读iOS 并发编程之 Operation Queues
NSOperation
准备工作
// 自定义一个类继承NSBlockOperation, // 重写相关的方法,观察声明周期@interface LKBlockOperation : NSBlockOperation @end@implementation LKBlockOperation- (void)dealloc { NSLog(@"LKBlockOperation dealloc"); }- (void)start { NSLog(@"Operation before execute super start"); [super start]; NSLog(@"Operation after execute super start"); }- (void)main { NSLog(@"Operation before execute super main"); [super main]; NSLog(@"Operation after execute super main"); } @end

操作执行过程
// 通过下面的代码和打印可知: // 把Operation加入到Queue中并不是立马就执行 // 执行过程: start -> main -> CompletionBlock -> dealloc- (void)testExecuteOperation { LKBlockOperation *operation = [[LKBlockOperation alloc] init]; [operation addExecutionBlock:^{NSLog(@"before execute operation"); NSInteger i = 0; while (i < 3) { NSLog(@"%@_%zd", [NSThread currentThread], ++i); }NSLog(@"after execute operation"); }]; [operation setCompletionBlock:^{ NSLog(@"completion"); }]; NSLog(@"before queue Add Operation"); [_queue addOperation:operation]; NSLog(@"after queue Add Operation"); }/* 2018-03-01 13:42:57.494851+0800 OperationQueues[3797:129659] before queue Add Operation 2018-03-01 13:42:57.495266+0800 OperationQueues[3797:129659] after queue Add Operation 2018-03-01 13:42:57.495350+0800 OperationQueues[3797:129708] Operation before execute super start 2018-03-01 13:42:57.495594+0800 OperationQueues[3797:129708] Operation before execute super main 2018-03-01 13:42:57.495940+0800 OperationQueues[3797:129708] before execute operation 2018-03-01 13:42:57.496402+0800 OperationQueues[3797:129708] {number = 3, name = (null)}_1 2018-03-01 13:42:57.497014+0800 OperationQueues[3797:129708] {number = 3, name = (null)}_2 2018-03-01 13:42:57.497155+0800 OperationQueues[3797:129708] {number = 3, name = (null)}_3 2018-03-01 13:42:57.497389+0800 OperationQueues[3797:129708] after execute operation 2018-03-01 13:42:57.497501+0800 OperationQueues[3797:129708] Operation after execute super main 2018-03-01 13:42:57.497645+0800 OperationQueues[3797:129708] Operation after execute super start 2018-03-01 13:42:57.497648+0800 OperationQueues[3797:129700] completion 2018-03-01 13:42:57.556400+0800 OperationQueues[3797:129700] LKBlockOperation dealloc */

如果任务还没加入到队列中就取消
// 通过下面的代码和打印可知: // 如果任务在没有加入到队列之前就取消,还是会调用start和CompletionBlock。但是main函数是不会执行了。 // 还可以看到在start方法执行之前,isCancel已经为YES, // 说明调用cancel方法之后,isCacel就会设置为YES,然后在根据是否取消来决定是否调用main方法- (void)testCancelBeforeAddToOperation { LKBlockOperation *operation = [[LKBlockOperation alloc] init]; [operation addExecutionBlock:^{ NSInteger i = 0; while (1) { NSLog(@"%@_%zd", [NSThread currentThread], i ++); } }]; [operation setCompletionBlock:^{ NSLog(@"completion"); }]; [operation cancel]; NSLog(@"before queue Add Operation"); [_queue addOperation:operation]; NSLog(@"after queue Add Operation"); }/* 2018-03-01 13:59:02.415212+0800 OperationQueues[4099:142821] before queue Add Operation 2018-03-01 13:59:02.415394+0800 OperationQueues[4099:142821] after queue Add Operation 2018-03-01 13:59:02.415428+0800 OperationQueues[4099:142863] self.cancel = 1 2018-03-01 13:59:02.415526+0800 OperationQueues[4099:142863] Operation before execute super start 2018-03-01 13:59:02.415739+0800 OperationQueues[4099:142866] completion 2018-03-01 13:59:02.415739+0800 OperationQueues[4099:142863] Operation after execute super start 2018-03-01 13:59:02.415907+0800 OperationQueues[4099:142863] LKBlockOperation dealloc */

任务的中途取消
// 通过下面的代码和打印可知: // 通过上面的测试我们知道当调用Cancel方法之后,isCancelled就会设置为YES, // 所以我们应该在每一次执行耗时操作或者是循环都应该判断下isCancelled,这也是苹果推荐的。 // 我把if(weakOperation.isCancelled) {break; }去掉之后,这个任务是不能正常取消的,就算调用了cancel方法。因为这个任务并没有结束的标志(return)- (void)testCancelOperationWhenExecuting { NSBlockOperation *operation = [[NSBlockOperation alloc] init]; __weak NSBlockOperation *weakOperation = operation; [operation addExecutionBlock:^{NSLog(@"before execute operation"); NSInteger i = 0; while (1) {NSLog(@"%@_%zd", [NSThread currentThread], i ++); if(weakOperation.isCancelled) { break; }if(i == 3) { [weakOperation cancel]; } }NSLog(@"after execute operation"); }]; [operation setCompletionBlock:^{ NSLog(@"completion"); }]; NSLog(@"before queue Add Operation"); [_queue addOperation:operation]; NSLog(@"after queue Add Operation"); }/* 2018-03-01 14:08:52.408137+0800 OperationQueues[4342:152805] before queue Add Operation 2018-03-01 14:08:52.408288+0800 OperationQueues[4342:152805] after queue Add Operation 2018-03-01 14:08:52.408332+0800 OperationQueues[4342:152842] before execute operation 2018-03-01 14:08:52.408608+0800 OperationQueues[4342:152842] {number = 3, name = (null)}_1 2018-03-01 14:08:52.408840+0800 OperationQueues[4342:152842] {number = 3, name = (null)}_2 2018-03-01 14:08:52.408988+0800 OperationQueues[4342:152842] {number = 3, name = (null)}_3 2018-03-01 14:08:52.409110+0800 OperationQueues[4342:152842] {number = 3, name = (null)}_4 2018-03-01 14:08:52.409197+0800 OperationQueues[4342:152842] after execute operation 2018-03-01 14:08:52.409339+0800 OperationQueues[4342:152852] completion */

NSOperationQueue
准备工作
- (NSBlockOperation *)createOperationRepeatCount:(NSInteger)count operationName:(NSString *)operationName { NSBlockOperation *operation = [[NSBlockOperation alloc] init]; operation.name = operationName; __weak NSBlockOperation *weakOperation = operation; [operation addExecutionBlock:^{NSLog(@"%@ before execute operation", weakOperation.name); NSInteger i = 0; while (i < count) { NSLog(@"%@-%zd", weakOperation.name, ++i); if(weakOperation.isCancelled) { break; } }NSLog(@"%@ after execute operation", weakOperation.name); }]; return operation; }

测试Queue的Cancel
// 通过下面的代码和打印可知: // 调用cancelAllOperations和调用Operation的cancel一致。相当于里面遍历Operation。然后调用cancel- (void)testQueueCancel { NSBlockOperation *operation0 = [self createOperationRepeatCount:NSIntegerMax operationName:@"operation0"]; NSBlockOperation *operation1 = [self createOperationRepeatCount:NSIntegerMax operationName:@"operation1"]; NSBlockOperation *operation2 = [self createOperationRepeatCount:NSIntegerMax operationName:@"operation2"]; [_queue addOperation:operation0]; [_queue addOperation:operation1]; [_queue addOperation:operation2]; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.01 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [_queue cancelAllOperations]; }); }/* 2018-03-01 15:26:50.856400+0800 OperationQueues[6275:233403] operation2 before execute operation 2018-03-01 15:26:50.856403+0800 OperationQueues[6275:233402] operation0 before execute operation 2018-03-01 15:26:50.856412+0800 OperationQueues[6275:233400] operation1 before execute operation 2018-03-01 15:26:50.856559+0800 OperationQueues[6275:233403] operation2-1 2018-03-01 15:26:50.856570+0800 OperationQueues[6275:233402] operation0-1 2018-03-01 15:26:50.856571+0800 OperationQueues[6275:233400] operation1-1 2018-03-01 15:26:50.856706+0800 OperationQueues[6275:233403] operation2-2 2018-03-01 15:26:50.856773+0800 OperationQueues[6275:233402] operation0-2 2018-03-01 15:26:50.856777+0800 OperationQueues[6275:233400] operation1-2 2018-03-01 15:26:50.856795+0800 OperationQueues[6275:233403] operation2-3 2018-03-01 15:26:50.856959+0800 OperationQueues[6275:233402] operation0-3 2018-03-01 15:26:50.857556+0800 OperationQueues[6275:233400] operation1-3 2018-03-01 15:26:50.857759+0800 OperationQueues[6275:233403] operation2-4 2018-03-01 15:26:50.858005+0800 OperationQueues[6275:233402] operation0-4 2018-03-01 15:26:50.858166+0800 OperationQueues[6275:233400] operation1-4 2018-03-01 15:26:50.858610+0800 OperationQueues[6275:233403] operation2-5 2018-03-01 15:26:50.858802+0800 OperationQueues[6275:233402] operation0-5 2018-03-01 15:26:50.858977+0800 OperationQueues[6275:233400] operation1-5 2018-03-01 15:26:50.859305+0800 OperationQueues[6275:233403] operation2-6 2018-03-01 15:26:50.920413+0800 OperationQueues[6275:233403] operation2 after execute operation 2018-03-01 15:26:50.920420+0800 OperationQueues[6275:233402] operation0 after execute operation 2018-03-01 15:26:50.920427+0800 OperationQueues[6275:233400] operation1 after execute operation */

测试Queue的Suspended
// 通过下面的代码和打印可知: // 先把setMaxConcurrentOperationCount设置为1, 让操作一个一个的执行 // 当operation0执行完的时候,就设置暂停,但是operation1还是会执行, // 但是operation2是不会执行,需要等待取消暂停,operation2才会继续执行 // 可以把Queue当成水管,Suspended属性当成水龙头,当水龙头关了, 后面的水就出不来(也就是不执行)- (void)testQueueSuspended { [_queue setMaxConcurrentOperationCount:1]; NSBlockOperation *operation0 = [self createOperationRepeatCount:3 operationName:@"operation0"]; NSBlockOperation *operation1 = [self createOperationRepeatCount:3 operationName:@"operation1"]; NSBlockOperation *operation2 = [self createOperationRepeatCount:3 operationName:@"operation2"]; __weak NSOperationQueue *weakQueue = _queue; [operation0 setCompletionBlock:^{NSLog(@"queue setSuspended YES"); [weakQueue setSuspended:YES]; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"after 1 second queue setSuspended NO"); [weakQueue setSuspended:NO]; }); }]; [_queue addOperation:operation0]; [_queue addOperation:operation1]; [_queue addOperation:operation2]; }/* 2018-03-01 15:28:45.414606+0800 OperationQueues[6346:235874] operation0 before execute operation 2018-03-01 15:28:45.414772+0800 OperationQueues[6346:235874] operation0-1 2018-03-01 15:28:45.414883+0800 OperationQueues[6346:235874] operation0-2 2018-03-01 15:28:45.414992+0800 OperationQueues[6346:235874] operation0-3 2018-03-01 15:28:45.415142+0800 OperationQueues[6346:235874] operation0 after execute operation 2018-03-01 15:28:45.415287+0800 OperationQueues[6346:235875] queue setSuspended YES 2018-03-01 15:28:45.415313+0800 OperationQueues[6346:235872] operation1 before execute operation 2018-03-01 15:28:45.415432+0800 OperationQueues[6346:235872] operation1-1 2018-03-01 15:28:45.415526+0800 OperationQueues[6346:235872] operation1-2 2018-03-01 15:28:45.415613+0800 OperationQueues[6346:235872] operation1-3 2018-03-01 15:28:45.415714+0800 OperationQueues[6346:235872] operation1 after execute operation 2018-03-01 15:28:46.508915+0800 OperationQueues[6346:235837] after 1 second queue setSuspended NO 2018-03-01 15:28:46.509152+0800 OperationQueues[6346:235872] operation2 before execute operation 2018-03-01 15:28:46.509272+0800 OperationQueues[6346:235872] operation2-1 2018-03-01 15:28:46.509368+0800 OperationQueues[6346:235872] operation2-2 2018-03-01 15:28:46.509455+0800 OperationQueues[6346:235872] operation2-3 2018-03-01 15:28:46.509564+0800 OperationQueues[6346:235872] operation2 after execute operation */

添加顺序对操作执行顺序的影响
// 通过下面的代码和打印可知: // 任务是按照FIFO的方式进行的, 也就是从先执行operation0 -> operation1 -> operation2 // 但是并不意味着operation0里面的任务代码会先执行// 举个例子: // 假如往queue中加入10个operation, 分别是operation0~operation9,依次顺序到队列 // 设置OperationCoun = 2, 这个时候先执行operation0和operation1 // 等opertaion0或者operation1其中一个执行完,接下来的一个执行的就是operation3,等下一个任务执行完,接下来执行的就是operation4- (void)testQueueConcurrentOperationCount { [_queue setMaxConcurrentOperationCount:2]; NSBlockOperation *operation0 = [self createOperationRepeatCount:2 operationName:@"operation0"]; NSBlockOperation *operation1 = [self createOperationRepeatCount:2 operationName:@"operation1"]; NSBlockOperation *operation2 = [self createOperationRepeatCount:2 operationName:@"operation2"]; [_queue addOperation:operation0]; [_queue addOperation:operation1]; [_queue addOperation:operation2]; }/* maxConcurrentOperationCount = 1 的情况2018-03-01 15:31:58.001022+0800 OperationQueues[6485:240841] operation0 before execute operation 2018-03-01 15:31:58.001177+0800 OperationQueues[6485:240841] operation0-1 2018-03-01 15:31:58.001363+0800 OperationQueues[6485:240841] operation0-2 2018-03-01 15:31:58.001473+0800 OperationQueues[6485:240841] operation0 after execute operation 2018-03-01 15:31:58.001622+0800 OperationQueues[6485:240830] operation1 before execute operation 2018-03-01 15:31:58.002013+0800 OperationQueues[6485:240830] operation1-1 2018-03-01 15:31:58.002187+0800 OperationQueues[6485:240830] operation1-2 2018-03-01 15:31:58.002287+0800 OperationQueues[6485:240830] operation1 after execute operation 2018-03-01 15:31:58.002428+0800 OperationQueues[6485:240829] operation2 before execute operation 2018-03-01 15:31:58.002528+0800 OperationQueues[6485:240829] operation2-1 2018-03-01 15:31:58.002623+0800 OperationQueues[6485:240829] operation2-2 2018-03-01 15:31:58.002709+0800 OperationQueues[6485:240829] operation2 after execute operation *//* maxConcurrentOperationCount = 2 的情况2018-03-01 15:33:10.568711+0800 OperationQueues[6522:242194] operation1 before execute operation 2018-03-01 15:33:10.568722+0800 OperationQueues[6522:242197] operation0 before execute operation 2018-03-01 15:33:10.568877+0800 OperationQueues[6522:242194] operation1-1 2018-03-01 15:33:10.568882+0800 OperationQueues[6522:242197] operation0-1 2018-03-01 15:33:10.568990+0800 OperationQueues[6522:242194] operation1-2 2018-03-01 15:33:10.568997+0800 OperationQueues[6522:242197] operation0-2 2018-03-01 15:33:10.569093+0800 OperationQueues[6522:242194] operation1 after execute operation 2018-03-01 15:33:10.569105+0800 OperationQueues[6522:242197] operation0 after execute operation 2018-03-01 15:33:10.569271+0800 OperationQueues[6522:242237] operation2 before execute operation 2018-03-01 15:33:10.569530+0800 OperationQueues[6522:242237] operation2-1 2018-03-01 15:33:10.569750+0800 OperationQueues[6522:242237] operation2-2 2018-03-01 15:33:10.569982+0800 OperationQueues[6522:242237] operation2 after execute operation */

Operation间的Dependency对操作执行顺序的影响
// 注意: 添加依赖需要在加入到队列之前,并且不要相互依赖,否则就造成死锁// 通过下面的代码和打印可知: // 添加了依赖的操作打破了上面(添加顺序对操作执行的影响)的规律,但是没有添加依赖的操作还是遵守的- (void)testQueueOperationDependency { [_queue setMaxConcurrentOperationCount:2]; NSBlockOperation *operation0 = [self createOperationRepeatCount:2 operationName:@"operation0"]; NSBlockOperation *operation1 = [self createOperationRepeatCount:2 operationName:@"operation1"]; NSBlockOperation *operation2 = [self createOperationRepeatCount:2 operationName:@"operation2"]; NSBlockOperation *operation3 = [self createOperationRepeatCount:2 operationName:@"operation3"]; NSBlockOperation *operation4 = [self createOperationRepeatCount:2 operationName:@"operation4"]; [operation0 addDependency:operation1]; [_queue addOperation:operation0]; [_queue addOperation:operation1]; [_queue addOperation:operation2]; [_queue addOperation:operation3]; [_queue addOperation:operation4]; }/* 2018-03-01 16:02:47.435604+0800 OperationQueues[7178:269608] operation2 before execute operation 2018-03-01 16:02:47.435608+0800 OperationQueues[7178:269616] operation1 before execute operation 2018-03-01 16:02:47.435778+0800 OperationQueues[7178:269608] operation2-1 2018-03-01 16:02:47.435782+0800 OperationQueues[7178:269616] operation1-1 2018-03-01 16:02:47.435881+0800 OperationQueues[7178:269608] operation2-2 2018-03-01 16:02:47.435918+0800 OperationQueues[7178:269616] operation1-2 2018-03-01 16:02:47.435997+0800 OperationQueues[7178:269608] operation2 after execute operation 2018-03-01 16:02:47.436063+0800 OperationQueues[7178:269616] operation1 after execute operation 2018-03-01 16:02:47.436164+0800 OperationQueues[7178:269606] operation3 before execute operation 2018-03-01 16:02:47.436196+0800 OperationQueues[7178:269608] operation0 before execute operation 2018-03-01 16:02:47.436314+0800 OperationQueues[7178:269606] operation3-1 2018-03-01 16:02:47.436816+0800 OperationQueues[7178:269608] operation0-1 2018-03-01 16:02:47.437014+0800 OperationQueues[7178:269606] operation3-2 2018-03-01 16:02:47.437182+0800 OperationQueues[7178:269608] operation0-2 2018-03-01 16:02:47.437341+0800 OperationQueues[7178:269606] operation3 after execute operation 2018-03-01 16:02:47.437520+0800 OperationQueues[7178:269608] operation0 after execute operation 2018-03-01 16:02:47.480951+0800 OperationQueues[7178:269606] operation4 before execute operation 2018-03-01 16:02:47.481082+0800 OperationQueues[7178:269606] operation4-1 2018-03-01 16:02:47.481191+0800 OperationQueues[7178:269606] operation4-2 2018-03-01 16:02:47.481290+0800 OperationQueues[7178:269606] operation4 after execute operation */

Operation间的Dependency
// 注意: 添加依赖需要在加入到队列之前,并且不要相互依赖,否则就造成死锁// 通过下面的代码和打印可知: // 就算设置MaxConcurrentOperationCount = 2,操作还是一个一个的执行 // 添加了依赖,顺序页修改了,请参照上面(Operation间的Dependency对操作执行顺序的影响)- (void)testQueueOperationDependency { [_queue setMaxConcurrentOperationCount:2]; NSBlockOperation *operation0 = [self createOperationRepeatCount:2 operationName:@"operation0"]; NSBlockOperation *operation1 = [self createOperationRepeatCount:2 operationName:@"operation1"]; NSBlockOperation *operation2 = [self createOperationRepeatCount:2 operationName:@"operation2"]; [operation1 addDependency:operation2]; [operation0 addDependency:operation1]; [_queue addOperation:operation0]; [_queue addOperation:operation1]; [_queue addOperation:operation2]; }/* 2018-03-01 15:56:03.770607+0800 OperationQueues[7053:264284] operation2 before execute operation 2018-03-01 15:56:03.770749+0800 OperationQueues[7053:264284] operation2-1 2018-03-01 15:56:03.770840+0800 OperationQueues[7053:264284] operation2-2 2018-03-01 15:56:03.770960+0800 OperationQueues[7053:264284] operation2 after execute operation 2018-03-01 15:56:03.771122+0800 OperationQueues[7053:264295] operation1 before execute operation 2018-03-01 15:56:03.771228+0800 OperationQueues[7053:264295] operation1-1 2018-03-01 15:56:03.771336+0800 OperationQueues[7053:264295] operation1-2 2018-03-01 15:56:03.771458+0800 OperationQueues[7053:264295] operation1 after execute operation 2018-03-01 15:56:03.771578+0800 OperationQueues[7053:264295] operation0 before execute operation 2018-03-01 15:56:03.771681+0800 OperationQueues[7053:264295] operation0-1 2018-03-01 15:56:03.771785+0800 OperationQueues[7053:264295] operation0-2 2018-03-01 15:56:03.771880+0800 OperationQueues[7053:264295] operation0 after execute operation */

补充
  • queuePriority属性只是在运行的时候,只是会资源的分配,并不会影响到上面所说的顺序

    推荐阅读