NSOperation和NSOperationQueue驗證筆記

NSOperation

準備工作
// 自定義一個類繼承NSBlockOperation,
// 重寫相關(guān)的方法,觀察聲明周期

@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

操作執(zhí)行過程
// 通過下面的代碼和打印可知: 
// 把Operation加入到Queue中并不是立馬就執(zhí)行
// 執(zhí)行過程: 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] <NSThread: 0x600000267600>{number = 3, name = (null)}_1
2018-03-01 13:42:57.497014+0800 OperationQueues[3797:129708] <NSThread: 0x600000267600>{number = 3, name = (null)}_2
2018-03-01 13:42:57.497155+0800 OperationQueues[3797:129708] <NSThread: 0x600000267600>{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
*/
如果任務(wù)還沒加入到隊列中就取消
// 通過下面的代碼和打印可知: 
// 如果任務(wù)在沒有加入到隊列之前就取消,還是會調(diào)用start和CompletionBlock。但是main函數(shù)是不會執(zhí)行了。
// 還可以看到在start方法執(zhí)行之前,isCancel已經(jīng)為YES,
// 說明調(diào)用cancel方法之后,isCacel就會設(shè)置為YES,然后在根據(jù)是否取消來決定是否調(diào)用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
*/
任務(wù)的中途取消
// 通過下面的代碼和打印可知: 
// 通過上面的測試我們知道當(dāng)調(diào)用Cancel方法之后,isCancelled就會設(shè)置為YES,
// 所以我們應(yīng)該在每一次執(zhí)行耗時操作或者是循環(huán)都應(yīng)該判斷下isCancelled,這也是蘋果推薦的。
// 我把if(weakOperation.isCancelled) {break;}去掉之后,這個任務(wù)是不能正常取消的,就算調(diào)用了cancel方法。因為這個任務(wù)并沒有結(jié)束的標志(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] <NSThread: 0x60c00026ee40>{number = 3, name = (null)}_1
2018-03-01 14:08:52.408840+0800 OperationQueues[4342:152842] <NSThread: 0x60c00026ee40>{number = 3, name = (null)}_2
2018-03-01 14:08:52.408988+0800 OperationQueues[4342:152842] <NSThread: 0x60c00026ee40>{number = 3, name = (null)}_3
2018-03-01 14:08:52.409110+0800 OperationQueues[4342:152842] <NSThread: 0x60c00026ee40>{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
// 通過下面的代碼和打印可知:
// 調(diào)用cancelAllOperations和調(diào)用Operation的cancel一致。相當(dāng)于里面遍歷Operation。然后調(diào)用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設(shè)置為1, 讓操作一個一個的執(zhí)行
// 當(dāng)operation0執(zhí)行完的時候,就設(shè)置暫停,但是operation1還是會執(zhí)行,
// 但是operation2是不會執(zhí)行,需要等待取消暫停,operation2才會繼續(xù)執(zhí)行
// 可以把Queue當(dāng)成水管,Suspended屬性當(dāng)成水龍頭,當(dāng)水龍頭關(guān)了, 后面的水就出不來(也就是不執(zhí)行)

- (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
*/
添加順序?qū)Σ僮鲌?zhí)行順序的影響
// 通過下面的代碼和打印可知: 
// 任務(wù)是按照FIFO的方式進行的, 也就是從先執(zhí)行operation0 -> operation1 -> operation2
// 但是并不意味著operation0里面的任務(wù)代碼會先執(zhí)行

// 舉個例子: 
// 假如往queue中加入10個operation, 分別是operation0~operation9,依次順序到隊列
// 設(shè)置OperationCoun = 2, 這個時候先執(zhí)行operation0和operation1
// 等opertaion0或者operation1其中一個執(zhí)行完,接下來的一個執(zhí)行的就是operation3,等下一個任務(wù)執(zhí)行完,接下來執(zhí)行的就是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對操作執(zhí)行順序的影響

// 注意: 添加依賴需要在加入到隊列之前,并且不要相互依賴,否則就造成死鎖

// 通過下面的代碼和打印可知:
// 添加了依賴的操作打破了上面(添加順序?qū)Σ僮鲌?zhí)行的影響)的規(guī)律,但是沒有添加依賴的操作還是遵守的


- (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
// 注意: 添加依賴需要在加入到隊列之前,并且不要相互依賴,否則就造成死鎖

// 通過下面的代碼和打印可知:
// 就算設(shè)置MaxConcurrentOperationCount = 2,操作還是一個一個的執(zhí)行
// 添加了依賴,順序頁修改了,請參照上面(Operation間的Dependency對操作執(zhí)行順序的影響)

- (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屬性只是在運行的時候,只是會資源的分配,并不會影響到上面所說的順序
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容