iOS 多線程

NSThread

1.NSThread共有3種創(chuàng)建方式

1.init
/// init 創(chuàng)建完成之后需要手動start
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(log) object:nil];
[thread start];
2.detachNewThreadSelector
/// 創(chuàng)建完成自動啟動
[NSThread detachNewThreadSelector:@selector(log) toTarget:self withObject:nil];
3.performSelectorInBackground
/// 創(chuàng)建完成自動啟動
[self performSelectorInBackground:@selector(log) withObject:nil];

2.NSThread類方法

    /// 獲取當(dāng)前線程 number = 1 為主線程 否則為子線程
    [NSThread currentThread];
    /// 線程休眠 單位秒
    [NSThread sleepForTimeInterval:2];
    /// 線程休眠到指定時間
    [NSThread sleepUntilDate: [NSDate date]];
    /// 獲取主線程
    [NSThread mainThread];
    /// 退出線程
    [NSThread exit];
    /// 判斷當(dāng)前線程是否為主線程
    [NSThread isMainThread];
    /// 判斷當(dāng)前線程是否為多線程
    [NSThread isMultiThreaded];

3.NSThread的屬性

/// 線程是否在執(zhí)行
@property (readonly, getter=isExecuting) BOOL executing;
/// 線程是否被取消
@property (readonly, getter=isFinished) BOOL finished;
/// 線程是否完成
@property (readonly, getter=isCancelled) BOOL cancelled;
/// 是否是主線程
@property (readonly) BOOL isMainThread;

GCD

GCD會自動利用更多的CPU內(nèi)核,自動管理線程的生命周期(創(chuàng)建陈轿、調(diào)度瓣履、銷毀)

1.GCD的基本概念

  • 任務(wù)(block):任務(wù)就是需要執(zhí)行的代碼塊淳梦。
  • 隊列:裝載線程任務(wù)的序列躺涝。
  • 并發(fā)隊列:同一時間疗我,隊列中的線程可以一起執(zhí)行仔夺,實質(zhì)是CPU在多條線程之前快速的切換琐脏。
  • 串行隊列:線程按照先進(jìn)先出的順序執(zhí)行。
  • 同步:不開啟新線程缸兔,一個接一個順序執(zhí)行日裙。
  • 異步:開啟多個線程,任務(wù)同一時間可以一起執(zhí)行惰蜜。

1.隊列的創(chuàng)建

/// 傳入兩個參數(shù)昂拂,第一個參數(shù)為隊列的標(biāo)識符,可為空抛猖,第二個參數(shù)用來表示創(chuàng)建串行隊列DISPATCH_QUEUE_SERIAL或者并發(fā)隊列DISPATCH_QUEUE_CONCURRENT
/// 串行隊列
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
/// 并發(fā)隊列
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
 ///  主隊列
dispatch_async(dispatch_get_global_queue(0, 0), ^{
   dispatch_async(dispatch_get_main_queue(), ^{
     });
});
/// 全局并發(fā)隊列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//全局并發(fā)隊列的優(yōu)先級
#define DISPATCH_QUEUE_PRIORITY_HIGH 2 // 高優(yōu)先級
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 // 默認(rèn)(中)優(yōu)先級
#define DISPATCH_QUEUE_PRIORITY_LOW (-2) // 低優(yōu)先級
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN // 后臺優(yōu)先級

2.同步異步的創(chuàng)建

同步:用dispatch_sync
異步:用dispatch_async

/// 同步執(zhí)行
dispatch_async(dispatch_get_global_queue(0, 0), ^{
     /// 代碼塊
});
/// 異步執(zhí)行
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
     /// 代碼塊
});

3.GCD的使用

由于CGD存在多種隊列和不同的執(zhí)行方式格侯,所以它們的組合方式有許多種。
1.串行同步
2.串行異步
3.并發(fā)同步
4.并發(fā)異步
5.主隊列同步
6.主隊列異步

  • 串行同步
    /// 創(chuàng)建串行隊列
    dispatch_queue_t queue = dispatch_queue_create("serial", DISPATCH_QUEUE_SERIAL);
    /// 創(chuàng)建同步線程
    dispatch_sync(queue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"串行同步1   %@",[NSThread currentThread]);
        }
    });
    dispatch_sync(queue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"串行同步2   %@",[NSThread currentThread]);
        }
    });

輸出結(jié)果都在主線程财著,順序執(zhí)行

串行同步1   <NSThread: 0x600001484980>{number = 1, name = main}
串行同步1   <NSThread: 0x600001484980>{number = 1, name = main}
串行同步2   <NSThread: 0x600001484980>{number = 1, name = main}
串行同步2   <NSThread: 0x600001484980>{number = 1, name = main}

  • 串行異步
    /// 創(chuàng)建串行隊列
    dispatch_queue_t queue = dispatch_queue_create("serial", DISPATCH_QUEUE_SERIAL);
    /// 創(chuàng)建異步線程
    dispatch_async(queue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"串行異步1   %@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"串行異步2   %@",[NSThread currentThread]);
        }
    });

輸出結(jié)果在子線程联四,順序執(zhí)行

串行異步1   <NSThread: 0x60000113ef80>{number = 5, name = (null)}
串行異步1   <NSThread: 0x60000113ef80>{number = 5, name = (null)}
串行異步2   <NSThread: 0x60000113ef80>{number = 5, name = (null)}
串行異步2   <NSThread: 0x60000113ef80>{number = 5, name = (null)}
  • 并發(fā)同步
    /// 創(chuàng)建并發(fā)隊列
    dispatch_queue_t queue = dispatch_queue_create("serial", DISPATCH_QUEUE_CONCURRENT);
    /// 創(chuàng)建同步線程
    dispatch_sync(queue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"并發(fā)同步1   %@",[NSThread currentThread]);
        }
    });
    dispatch_sync(queue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"并發(fā)同步2   %@",[NSThread currentThread]);
        }
    });

輸出結(jié)果都在主線程,順序執(zhí)行

并發(fā)同步1   <NSThread: 0x600000ca0540>{number = 1, name = main}
并發(fā)同步1   <NSThread: 0x600000ca0540>{number = 1, name = main}
并發(fā)同步2   <NSThread: 0x600000ca0540>{number = 1, name = main}
并發(fā)同步2   <NSThread: 0x600000ca0540>{number = 1, name = main}
  • 并發(fā)異步
    /// 創(chuàng)建并發(fā)隊列
    dispatch_queue_t queue = dispatch_queue_create("serial", DISPATCH_QUEUE_CONCURRENT);
    /// 創(chuàng)建異步線程
    dispatch_async(queue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"并發(fā)異步1   %@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"并發(fā)異步2   %@",[NSThread currentThread]);
        }
    });

輸出結(jié)果在不同子線程撑教,無序執(zhí)行

并發(fā)異步1   <NSThread: 0x600002d34440>{number = 5, name = (null)}
并發(fā)異步2   <NSThread: 0x600002d4bb80>{number = 3, name = (null)}
并發(fā)異步2   <NSThread: 0x600002d4bb80>{number = 3, name = (null)}
并發(fā)異步1   <NSThread: 0x600002d34440>{number = 5, name = (null)}

  • 主隊列同步
    /// 獲取主隊列
    dispatch_queue_t queue = dispatch_get_main_queue();
    /// 創(chuàng)建同步線程
    dispatch_sync(queue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"主隊列同步1   %@",[NSThread currentThread]);
        }
    });
    dispatch_sync(queue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"主隊列同步2   %@",[NSThread currentThread]);
        }
    });

程序崩潰朝墩,原因是把任務(wù)放到主線程隊列中,它就會立即執(zhí)行伟姐,而主線程正在處理syncMain方法鱼辙,任務(wù)需要等待主隊列執(zhí)行完才能執(zhí)行廉嚼,syncMain執(zhí)行到第一個任務(wù)時,又要等待第一個任務(wù)完成才能繼續(xù)執(zhí)行倒戏,這樣就形成了死鎖怠噪。

  • 主隊列異步
    /// 獲取主隊列
    dispatch_queue_t queue = dispatch_get_main_queue();
    /// 創(chuàng)建異步線程
    dispatch_async(queue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"主隊列異步1   %@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"主隊列異步2   %@",[NSThread currentThread]);
        }
    });

輸出結(jié)果在主線程,順序執(zhí)行杜跷。

主隊列異步1   <NSThread: 0x600003844200>{number = 1, name = main}
主隊列異步1   <NSThread: 0x600003844200>{number = 1, name = main}
主隊列異步2   <NSThread: 0x600003844200>{number = 1, name = main}
主隊列異步2   <NSThread: 0x600003844200>{number = 1, name = main}
  • GCD線程之間通訊
    開發(fā)中會經(jīng)常需要做耗時操作的任務(wù)傍念,比如網(wǎng)絡(luò)請求,圖片下載等葛闷,當(dāng)完成耗時操作后需要回到主線程更新UI憋槐,就需要用到線程之間的通訊。
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_async(queue, ^{
        // 模擬圖片下載
        NSLog(@"開始下載圖片");
        [NSThread sleepForTimeInterval:3];
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"更新UI");
        });
    });

會新開辟子線程下載淑趾,下載完成回到主線程刷新UI阳仔。

  • CGD柵欄
    當(dāng)我們需要異步執(zhí)行任務(wù),而任務(wù)又分為兩組執(zhí)行扣泊,A執(zhí)行完成之后才能執(zhí)行B近范,這時候就可以用到GCD的柵欄dispatch_barrier_async
    /// 創(chuàng)建并發(fā)隊列
    dispatch_queue_t queue = dispatch_queue_create("concurrent", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"柵欄異步1-%d   %@",i,[NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"柵欄異步2-%d   %@",i,[NSThread currentThread]);
        }
    });
    
    dispatch_barrier_async(queue, ^{
        NSLog(@"并發(fā)異步執(zhí)行");
    });
    
    dispatch_async(queue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"柵欄異步3-%d   %@",i,[NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"柵欄異步4-%d   %@",i,[NSThread currentThread]);
        }
    });

運(yùn)行結(jié)果:開啟多條線程,所有任務(wù)并發(fā)執(zhí)行延蟹,但3,4在1,2之后才會執(zhí)行

柵欄異步1-0   <NSThread: 0x60000352e940>{number = 7, name = (null)}
柵欄異步2-0   <NSThread: 0x60000352c540>{number = 6, name = (null)}
柵欄異步1-1   <NSThread: 0x60000352e940>{number = 7, name = (null)}
柵欄異步2-1   <NSThread: 0x60000352c540>{number = 6, name = (null)}
并發(fā)異步執(zhí)行
柵欄異步4-0   <NSThread: 0x60000352e940>{number = 7, name = (null)}
柵欄異步3-0   <NSThread: 0x60000352c540>{number = 6, name = (null)}
柵欄異步3-1   <NSThread: 0x60000352c540>{number = 6, name = (null)}
柵欄異步4-1   <NSThread: 0x60000352e940>{number = 7, name = (null)}
  • GCD延時執(zhí)行
    當(dāng)我們需要等待一段時間后才執(zhí)行某個任務(wù)時就會用到延時執(zhí)行dispatch_after
    NSLog(@"開始");
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 4), dispatch_get_main_queue(), ^{
        NSLog(@"4秒后執(zhí)行");
    });
  • GCD隊列組
    當(dāng)我們需要異步執(zhí)行多個任務(wù)评矩,且當(dāng)所有任務(wù)完成后需要做某些操作,就需要用到隊列組阱飘。
    /// 創(chuàng)建一個隊列組
    dispatch_group_t group = dispatch_group_create();
    
    dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
        NSLog(@"耗時操作1 %@",[NSThread currentThread]);
    });
    dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
        NSLog(@"耗時操作2 %@",[NSThread currentThread]);
    });
    dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
        NSLog(@"耗時操作3 %@",[NSThread currentThread]);
    });
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"主線程刷新 %@",[NSThread currentThread]);
    });

運(yùn)行結(jié)果為

耗時操作3 <NSThread: 0x600001461240>{number = 7, name = (null)}
耗時操作2 <NSThread: 0x600001460b80>{number = 5, name = (null)}
耗時操作1 <NSThread: 0x6000014362c0>{number = 4, name = (null)}
主線程刷新 <NSThread: 0x6000014241c0>{number = 1, name = main}
  • GCD對循環(huán)任務(wù)的處理
    /// GCD循環(huán)任務(wù)處理
    dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t i) {
        NSLog(@"GCD循環(huán)任務(wù)處理   %@:%zu",[NSThread currentThread],i);
    });

輸出結(jié)果是由不同線程完成的斥杜,有效的提升了循環(huán)的效率

GCD循環(huán)任務(wù)處理   <NSThread: 0x600001e7db40>{number = 3, name = (null)}:2
GCD循環(huán)任務(wù)處理   <NSThread: 0x600001e79c40>{number = 6, name = (null)}:1
GCD循環(huán)任務(wù)處理   <NSThread: 0x600001e3c1c0>{number = 1, name = main}:0
GCD循環(huán)任務(wù)處理   <NSThread: 0x600001e1bb40>{number = 5, name = (null)}:3
GCD循環(huán)任務(wù)處理   <NSThread: 0x600001e7db40>{number = 3, name = (null)}:4
GCD循環(huán)任務(wù)處理   <NSThread: 0x600001e3c1c0>{number = 1, name = main}:5
GCD循環(huán)任務(wù)處理   <NSThread: 0x600001e79c40>{number = 6, name = (null)}:6
GCD循環(huán)任務(wù)處理   <NSThread: 0x600001e1bb40>{number = 5, name = (null)}:7
GCD循環(huán)任務(wù)處理   <NSThread: 0x600001e7db40>{number = 3, name = (null)}:8
GCD循環(huán)任務(wù)處理   <NSThread: 0x600001e3c1c0>{number = 1, name = main}:9
  • GCD中的消息與信號
    GCD框架中提供了dispatch_source_t的對象,用來接收和傳遞某個消息沥匈,之后執(zhí)行對應(yīng)的代碼塊
    /// 創(chuàng)建數(shù)據(jù)對象
    dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_main_queue());
    /// 接收數(shù)據(jù)變化
    dispatch_source_set_event_handler(source, ^{
        NSLog(@"收到數(shù)據(jù) %lu",dispatch_source_get_data(source));
    });
    /// 啟動
    dispatch_resume(source);
    /// 設(shè)置數(shù)據(jù)
    dispatch_source_merge_data(source, 1);

輸出:

收到數(shù)據(jù) 1

GCD還有一個概念是信號量蔗喂,它的用法與消息類似

    /// 創(chuàng)建一個信號 初始值為0
    dispatch_semaphore_t singer = dispatch_semaphore_create(0);
    /// 發(fā)送信號
    dispatch_semaphore_signal(singer);
    /// 等待信號,當(dāng)信號大于0執(zhí)行后面的代碼高帖,否則等待(會阻塞當(dāng)前線程),第二個參數(shù)為等待時間弱恒,DISPATCH_TIME_FOREVER為一直等待
    dispatch_semaphore_wait(singer, DISPATCH_TIME_FOREVER);
    NSLog(@"執(zhí)行");

注:每次執(zhí)行過等待信號后,信號量會減1

NSOperation

NSOperation可以理解為任務(wù)操作棋恼,是基于Objective-C封裝的一套管理與執(zhí)行線程操作的類,這個類是一個抽象類锈玉,通常我們會使用NSInvocationOperation和NSBlockOperation這兩個子類進(jìn)行開發(fā)爪飘,或者也可以繼承于NSOperation類,封裝我們自己的類拉背。

  • NSInvocationOperation
    /// 創(chuàng)建NSInvocationOperation
    NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationOperation) object:nil];
    /// 啟動NSInvocationOperation
    [invocationOperation start];

輸出結(jié)果:并未開啟新的線程

當(dāng)前線程 <NSThread: 0x60000184c640>{number = 1, name = main}
  • NSBlockOperation
    /// NSBlockOperation
    NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"當(dāng)前線程 %@",[NSThread currentThread]);
    }];
    /// 啟動NSBlockOperation
    [operation start];

輸出結(jié)果:并未開啟新的線程

當(dāng)前線程 <NSThread: 0x6000003a07c0>{number = 1, name = main}

直接使用NSInvocationOperation和NSBlockOperation不會開啟新的線程师崎,但是可以使用NSBlockOperation的addExecutionBlock方法開啟新的線程

    /// NSBlockOperation
    NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"當(dāng)前線程 %@",[NSThread currentThread]);
    }];
    [operation addExecutionBlock:^{
        NSLog(@"方法一當(dāng)前線程 %@",[NSThread currentThread]);
    }];
    [operation addExecutionBlock:^{
        NSLog(@"方法二當(dāng)前線程 %@",[NSThread currentThread]);
    }];
    /// 啟動NSBlockOperation
    [operation start];

輸出結(jié)果:通過addExecutionBlock的方法開啟了新的線程

當(dāng)前線程 <NSThread: 0x60000230c1c0>{number = 1, name = main}
方法一當(dāng)前線程 <NSThread: 0x600002342480>{number = 8, name = (null)}
方法二當(dāng)前線程 <NSThread: 0x60000230f9c0>{number = 7, name = (null)}
  • 繼承NSOperation的子類
    首先我們繼承NSOperation,然后重新它的main方法
#import "MyOperation.h"

@implementation MyOperation

- (void)main{
    NSLog(@"當(dāng)前線程%@",[NSThread currentThread]);
}

@end

使用:

    /// 創(chuàng)建自定義的Operation
    MyOperation *operation = [[MyOperation alloc] init];
    [operation start];

結(jié)果:并未開啟新的線程

當(dāng)前線程<NSThread: 0x60000193c7c0>{number = 1, name = main}

總結(jié):NSOperation是需要配合使用NSOperationQueue來實現(xiàn)多線程的

  • NSOperationQueue
    NSOperationQueue有兩種隊列(主隊列椅棺、其他隊列)
    /// 主隊列
    NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
    
    /// 其他隊列,默認(rèn)并發(fā)犁罩,開啟多線程
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];

NSOperationQueue有一個參數(shù)maxConcurrentOperationCount最大并發(fā)數(shù)量齐蔽,默認(rèn)為-1,并發(fā)執(zhí)行床估,當(dāng)maxConcurrentOperationCount為1時含滴,則表示串行執(zhí)行,當(dāng)maxConcurrentOperationCount大于1時丐巫,則表示并發(fā)執(zhí)行谈况。

  • NSOperation+NSOperationQueue
    創(chuàng)建NSInvocationOperation和NSBlockOperation,并把它們加入隊列递胧。
    /// 創(chuàng)建隊列碑韵,默認(rèn)并發(fā)執(zhí)行
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    /// 創(chuàng)建NSInvocationOperation
    NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationOperation) object:nil];
    
    /// NSBlockOperation
    NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"當(dāng)前線程 %@",[NSThread currentThread]);
    }];
    [queue addOperation:invocationOperation];
    [queue addOperation:operation];

結(jié)果:都是在子線程執(zhí)行的,開啟了新線程缎脾。

當(dāng)前線程 <NSThread: 0x600003a1b540>{number = 5, name = (null)}
當(dāng)前線程 <NSThread: 0x600003a19a00>{number = 6, name = (null)}

直接加入隊列

    /// 創(chuàng)建隊列祝闻,默認(rèn)并發(fā)執(zhí)行
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    [queue addOperationWithBlock:^{
        NSLog(@"當(dāng)前線程 %@",[NSThread currentThread]);
    }];

結(jié)果:子線程執(zhí)行,開啟了新線程遗菠。

當(dāng)前線程 <NSThread: 0x600003a6ab80>{number = 7, name = (null)}

NSOperationQueue的串行與并行

    /// 創(chuàng)建隊列联喘,默認(rèn)并發(fā)執(zhí)行
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    /// 設(shè)置最大并發(fā)數(shù)
    queue.maxConcurrentOperationCount = 1;
    
    [queue addOperationWithBlock:^{
        NSLog(@"當(dāng)前線程1 %@",[NSThread currentThread]);
    }];
    
    [queue addOperationWithBlock:^{
        NSLog(@"當(dāng)前線程2 %@",[NSThread currentThread]);
    }];
    
    [queue addOperationWithBlock:^{
        NSLog(@"當(dāng)前線程3 %@",[NSThread currentThread]);
    }];

當(dāng)maxConcurrentOperationCount設(shè)置為1時,開啟了新的線程且串行執(zhí)行舷蒲。

當(dāng)前線程1 <NSThread: 0x600001e77340>{number = 6, name = (null)}
當(dāng)前線程2 <NSThread: 0x600001e77340>{number = 6, name = (null)}
當(dāng)前線程3 <NSThread: 0x600001e77340>{number = 6, name = (null)}

當(dāng)maxConcurrentOperationCount大于1時耸袜,開啟了新的線程且并發(fā)執(zhí)行。

當(dāng)前線程2 <NSThread: 0x6000004050c0>{number = 6, name = (null)}
當(dāng)前線程1 <NSThread: 0x6000004048c0>{number = 4, name = (null)}
當(dāng)前線程3 <NSThread: 0x60000044b340>{number = 5, name = (null)}
  • NSOperation操作依賴
    當(dāng)我們在執(zhí)行線程任務(wù)時牲平,B需要等待A執(zhí)行完成之后才能執(zhí)行堤框,這時候就可以使用NSOperation的操作依賴。
    /// 創(chuàng)建隊列纵柿,默認(rèn)并發(fā)執(zhí)行
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    /// 操作1
    NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationOperation) object:nil];
    
    /// 操作2
    NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@" 操作2當(dāng)前線程 %@",[NSThread currentThread]);
    }];
    /// 操作1需要等待操作2執(zhí)行完成
    [invocationOperation addDependency:operation];
    [queue addOperation:invocationOperation];
    [queue addOperation:operation];

結(jié)果:操作2完成之后才會執(zhí)行操作1

操作2當(dāng)前線程 <NSThread: 0x6000030544c0>{number = 7, name = (null)}
操作1當(dāng)前線程 <NSThread: 0x6000030544c0>{number = 7, name = (null)}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蜈抓,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子昂儒,更是在濱河造成了極大的恐慌沟使,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件渊跋,死亡現(xiàn)場離奇詭異腊嗡,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)拾酝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門燕少,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蒿囤,你說我怎么就攤上這事客们。” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵底挫,是天一觀的道長恒傻。 經(jīng)常有香客問我,道長建邓,這世上最難降的妖魔是什么盈厘? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮涝缝,結(jié)果婚禮上扑庞,老公的妹妹穿的比我還像新娘。我一直安慰自己拒逮,他們只是感情好罐氨,可當(dāng)我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著滩援,像睡著了一般栅隐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上玩徊,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天租悄,我揣著相機(jī)與錄音,去河邊找鬼恩袱。 笑死泣棋,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的畔塔。 我是一名探鬼主播潭辈,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼澈吨!你這毒婦竟也來了凭舶?” 一聲冷哼從身側(cè)響起铛铁,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎沮明,沒想到半個月后珍促,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體闰蛔,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡唆樊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年港华,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蚣录。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡割择,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出包归,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布公壤,位于F島的核電站换可,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏厦幅。R本人自食惡果不足惜沾鳄,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望确憨。 院中可真熱鬧译荞,春花似錦、人聲如沸休弃。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽塔猾。三九已至篙骡,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間丈甸,已是汗流浹背糯俗。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留睦擂,地道東北人得湘。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像顿仇,于是被迫代替她去往敵國和親淘正。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容