ios - 多線程之十一:NSOperation

NSOperation 是抽象類,所以要用子類來進行線程使用;目前是三種方式:
1:NSInvocationOperation
2:NSBlockOperation
3:創建子類繼承自 NSOperation

###1:NSInvocationOperation
NSInvocationOperation : 可以理解爲是一個爲 @selector 包裝上任務特性,能夠在子線程中執行;

注意事項:
1:單獨使用 NSInvocationOperation 不會開啓新線程,會在主線程中執行,造成主線程堵塞;
2:創建 NSInvocationOperation 對象之後需要手動開啓 start ;
3:此時要結合 NSOperationQueue 組合使用;(衍生出一個問題:GCD , operation 都體現出了線程隊列的概念,隊列對多線程來說爲什麼如此重要?)
4:在和 NSOperationQueue 組合使用時,不用再通過 start 進行開啓;
5:NSInvocationOperation 的使用一定要結合着 NSOperationQueue

代碼展示:

NSInvocationOperation *InvocationOperationA = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run_invocationOperation) object:nil];
InvocationOperationA.name = @"operation - invocationA";

NSInvocationOperation *InvocationOperationB = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run_invocationOperation) object:nil];
InvocationOperationB.name = @"operation - invocationB";

NSInvocationOperation *InvocationOperationC = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run_invocationOperation) object:nil];
InvocationOperationC.name = @"operation - invocationC";

NSInvocationOperation *InvocationOperationD = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run_invocationOperation) object:nil];
InvocationOperationD.name = @"operation - invocationD";

NSInvocationOperation *InvocationOperationE = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run_invocationOperation) object:nil];
InvocationOperationE.name = @"operation - invocationE";


//啓動任務 :NSInvocationOperation 在和 NSOperationQueue 組合使用時,不需要使用 start
//    [InvocationOperationA start];
//    [InvocationOperationB start];
//    [InvocationOperationC start];
//    [InvocationOperationD start];

//NSOperationQueue - 線程隊列
NSOperationQueue *operQueue = [[NSOperationQueue alloc] init];

operQueue.name = @"oper_Queue";   //設置隊列名稱

[operQueue setMaxConcurrentOperationCount:2]; //設置隊列中允許的最大線程併發數


//判斷任務的執行狀態
if (InvocationOperationA.isExecuting) {
    NSLog(@"InvocationOperationA 是執行");
}else{
    NSLog(@"InvocationOperationA 還未執行");
}


/*
 NSOperationQueue - 兩種在隊列中添加事務的方式;
    1 : addOperation
    2 : addOperationWithBlock
 */
//在隊列中添加要執行的操作或者任務;
[operQueue addOperation:InvocationOperationA];
[operQueue addOperation:InvocationOperationB];
[operQueue addOperation:InvocationOperationC];
[operQueue addOperation:InvocationOperationD];
[operQueue addOperation:InvocationOperationE];

###2:NSBlockOperation
NSBlockOperation 兩種方式存放任務代碼

1:類方法 - blockOperationWithBlock
* 2:實例方法 - addExecutionBlock*

注意事項:
1:單獨使用 NSBlockOperation 設置一個 addExecutionBlock 不會開啓新線程,會在主線程中執行,造成主線程堵塞;
2:單獨使用 NSBlockOperation 中的 addExecutionBlock 方式設置任務時候,任務除在主線程執行之外,還會開啓新的子線程來執行其他任務。但還是會造成主線程的堵塞;
3:創建 NSBlockOperation 對象之後需要手動開啓 start ;
4:此時要結合 NSOperationQueue 組合使用;(衍生出一個問題:GCD , operation 都體現出了線程隊列的概念,隊列對多線程來說爲什麼如此重要?)
5:在和 NSOperationQueue 組合使用時,不用再通過 start 進行開啓;

實現組合兩種: 都不會主線程的堵塞

第一種 :
1 :NSBlockOperation 實例化一個對象;
2 :使用 addExecutionBlock 添加要執行的任務代碼;可以添加多個;
3 :創建 NSOperationQueue 實例
4 :在 NSOperationQueue 實例中添加 NSBlockOperation 實例
5 :不要再使用 start 進行啓動了

優缺點:
(優點)1:不用創建很多 NSBlockOperation 對象,在一個對象添加多個任務的 block 代碼;
(優點)2:會根據 NSBlockOperation 中添加的 block 塊數量開啓相應多的子線程執行任務;(需要思考的問題:爲什麼會根據 block 開啓子線程?是不是和 block 的實現原理有關,或者和其特殊屬性有關?)
(缺點)3:通過 NSOperationQueue 的函數 setMaxConcurrentOperationCount 控制併發數是不生效的;

第二種 :
1 :NSBlockOperation 實例化多個對象;
2 :在每個 NSBlockOperation 實例對象中設置任務的 block 代碼;
3 :創建 NSOperationQueue 實例
4 :在 NSOperationQueue 實例中添加所有要使用的 NSBlockOperation 實例
5 :不要再使用 start 進行啓動了

優缺點:
(優點)1:可以通過 NSOperationQueue 的函數 setMaxConcurrentOperationCount 控制併發數;
(優點)2:會根據 NSBlockOperation 的 setMaxConcurrentOperationCount 設置數量開啓相應多的子線程執行任務;
(缺點)3:需要爲每一個任務的 block 塊創建一個 NSBlockOperation 實例;

代碼展示:

[NSBlockOperation blockOperationWithBlock:^{
    for (int i = 0; i <= 10 ; i ++) {
        sleep(1);
        NSLog(@"類方法 blockOperation - 當前線程名稱:%@ ——%d",[NSThread currentThread].name,i);
    }
}];


NSBlockOperation *blockOperation = [[NSBlockOperation alloc] init];
blockOperation.name = @"blockOperation";
[blockOperation addExecutionBlock:^{
    for (int i = 0; i <= 10 ; i ++) {
        sleep(1);
        NSLog(@"blockOperation - 當前線程名稱:%@ ——%d",[NSThread currentThread].name,i);
    }
}];
[blockOperation addExecutionBlock:^{
    for (int i = 20; i <= 30 ; i ++) {
        sleep(1);
        NSLog(@"blockOperation - 當前線程名稱:%@ ——%d",[NSThread currentThread].name,i);
    }
}];
[blockOperation addExecutionBlock:^{
    for (int i = 30; i <= 40 ; i ++) {
        sleep(1);
        NSLog(@"blockOperation - 當前線程名稱:%@ ——%d",[NSThread currentThread].name,i);
    }
}];

//    [blockOperation start];




NSBlockOperation *blockOperationA = [[NSBlockOperation alloc] init];
blockOperationA.name = @"blockOperationA";
[blockOperationA addExecutionBlock:^{
    for (int i = 0; i <= 10 ; i ++) {
        sleep(1);
        NSLog(@"blockOperationA - 當前線程名稱:%@ ——%d",[NSThread currentThread].name,i);
    }
}];
NSBlockOperation *blockOperationB = [[NSBlockOperation alloc] init];
blockOperationB.name = @"blockOperationB";
[blockOperationB addExecutionBlock:^{
    for (int i = 0; i <= 10 ; i ++) {
        sleep(1);
        NSLog(@"blockOperationB - 當前線程名稱:%@ ——%d",[NSThread currentThread].name,i);
    }
}];
NSBlockOperation *blockOperationC = [[NSBlockOperation alloc] init];
blockOperationC.name = @"blockOperationC";
[blockOperationC addExecutionBlock:^{
    for (int i = 0; i <= 10 ; i ++) {
        sleep(1);
        NSLog(@"blockOperationC - 當前線程名稱:%@ ——%d",[NSThread currentThread].name,i);
    }
}];

NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
[operationQueue setMaxConcurrentOperationCount:2];
//方式一
[operationQueue addOperation:blockOperation];



//方式二
//    [operationQueue addOperation:blockOperationA];
//    [operationQueue addOperation:blockOperationB];
//    [operationQueue addOperation:blockOperationC];

3:創建子類繼承自 NSOperation

//NSOperation - 自定義線程 - CustomOperation
*
個人解讀:
所謂的自定義線程就是在繼承 NSOperation 之後,在類內部設置好該類要執行的特定任務,也就是說自定線程,自定義的部分就是明確該線程要執行的操作是什麼。
*

1:創建文件

自定義線程

2:在.m文件重寫 main 方法

  - (void) main {

//NSOperation - 自定義使用實現子線程操作 - 同步任務

NSLog(@"線程名字 = %@",self.name);
for (int i = 0; i <= 10 ; i ++) {
    sleep(1);
    NSLog(@"自定義線程 %@——%d",self.name,i);
}
}

3:如果在自定義 NSOperation 時 自定義使用實現子線程操作 - 異步任務,

NSLog(@"開始任務");
dispatch_async(dispatch_get_global_queue(0, 0), ^{

    if (self.isCancelled) {
        return ;
    }
    for (int i = 0; i <= 10 ; i ++) {
        sleep(1);
        NSLog(@"自定義線程 ——%d",i);
    }
    NSLog(@"循環結束");
    self.over = YES;
});
NSLog(@"結束任務");

執行結果:

自定義線程,異步任務

該如何解決:

NSLog(@"開始任務");
dispatch_async(dispatch_get_global_queue(0, 0), ^{

    if (self.isCancelled) {
        return ;
    }
    for (int i = 0; i <= 10 ; i ++) {
        sleep(1);
        NSLog(@"自定義線程 ——%d",i);
    }
    NSLog(@"循環結束");
    self.over = YES;
});

//解決辦法 : 使用 NSRunLoop 進行循環執行
/*
    通過 NSRunLoop 的控制線程,等待異步任務完成之後再讓該線程執行完成。
 */
while (!self.over && !self.isCancelled) {
    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}


NSLog(@"結束任務");

NSOperation有一個很大的便捷就是處理線程依賴:

CustomOperation *operation1 = [[CustomOperation alloc] initWithName:@"自定義線程一"];
CustomOperation *operation2 = [[CustomOperation alloc] initWithName:@"自定義線程二"];
CustomOperation *operation3 = [[CustomOperation alloc] initWithName:@"自定義線程三"];
CustomOperation *operation4 = [[CustomOperation alloc] initWithName:@"自定義線程四"];
CustomOperation *operation5 = [[CustomOperation alloc] initWithName:@"自定義線程五"];

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.name = @"自定義線程的所在隊列";
[queue setMaxConcurrentOperationCount:2];



//NSOperation - 線程依賴 Dependency(依賴)
/*
 添加上依賴之後,兩個線程就有了依賴關係,舉例說明, A 設置了依賴 B ,帶代碼執行階段時, 只有當 B 執行完之後纔會執行 A;

 應用場景:
 在實際開發過程中,任務3 之後再任務1和任務2完成之後才能執行就可以使用依賴關係

 注意事項:
 再設置依賴關係的時候要注意不要添加依賴關係形成閉環,出現互相依賴的情況,會導致程序不執行;
 */
[operation1 addDependency:operation5];
[operation5 addDependency:operation2];
[operation2 addDependency:operation4];
[operation4 addDependency:operation3];
//    [operation3 addDependency:operation1];  這一點一定要注意,千萬要捋順彼此的依賴關係

//一對多的依賴關係
/*
    只有當多個有依賴的任務執行完成之後纔會執行
 */
//    [operation1 addDependency:operation5];
//    [operation1 addDependency:operation2];
//    [operation2 addDependency:operation4];
//    [operation4 addDependency:operation3];


[queue addOperation:operation1];
[queue addOperation:operation2];
[queue addOperation:operation3];
[queue addOperation:operation4];
[queue addOperation:operation5];
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章