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)}