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属性只是在运行的时候,只是会资源的分配,并不会影响到上面所说的顺序

你可能感兴趣的:(NSOperation和NSOperationQueue验证笔记)