- 本文主要探索
NSThread,GCD,NSOperation這三種實現(xiàn)多線程的方式;
NSThread
- NSthread是蘋果官方提供面向?qū)ο蟮木€程操作技術(shù),是對thread的上層封裝,比較偏向于底層,簡單方便,可以直接操作線程對象,使用頻率較少;
NSThread的初始化創(chuàng)建
【第一種】:alloc方法
- (void)viewDidLoad {
[super viewDidLoad];
//第一種方式: alloc 需手動開啟
YYThread *thread = [[YYThread alloc]initWithTarget:self selector:@selector(task:) object:@"thread_name_01"];
[thread start];
}
- (void)task:(NSObject *)obj{
NSLog(@"%@ - %@", obj, [NSThread currentThread]);
}
- LLDB調(diào)試結(jié)果:

Snip20210323_24.png
- YYThread自定義線程繼承自NSThread;
- 會創(chuàng)建子線程執(zhí)行任務(wù)(task:方法);
- 需要手動的啟動,手動調(diào)用start方法;
【第二種】:detachNewThreadSelector類方法
- (void)viewDidLoad {
[super viewDidLoad];
[NSThread detachNewThreadSelector:@selector(task:) toTarget:self withObject:@"thread_name_02"];
}
- (void)task:(NSObject *)obj{
NSLog(@"%@ - %@", obj, [NSThread currentThread]);
}
- LLDB調(diào)試結(jié)果:

Snip20210323_25.png
- detachNewThreadSelector類方法會生成子線程執(zhí)行任務(wù)(task:方法);
【第三種】:performSelector方法
- 調(diào)用performSelectorOnMainThread方法,若當(dāng)前線程是主線程,會在主線程中立刻執(zhí)行selector任務(wù)方法;
- (void)viewDidLoad {
[super viewDidLoad];
//當(dāng)前線程為主線程
[self performSelectorOnMainThread:@selector(task_perform:) withObject:@"thread_name_04" waitUntilDone:YES];
}
- (void)task_perform:(NSObject *)obj{
NSLog(@"%@ - %@", obj, [NSThread currentThread]);
}

Snip20210323_26.png
- 調(diào)用performSelectorOnMainThread方法,
若當(dāng)前線程是子線程,waitUntilDone = YES時,會阻塞當(dāng)前子線程,當(dāng)主線程執(zhí)行完selector任務(wù)方法時,解除子線程的阻塞,繼續(xù)執(zhí)行子線程中的任務(wù);
- (void)viewDidLoad {
[super viewDidLoad];
YYThread *thread = [[YYThread alloc]initWithTarget:self selector:@selector(task:) object:@"thread_name_01"];
[thread start];
}
- (void)task:(NSObject *)obj{
NSLog(@"%@ - %@", obj, [NSThread currentThread]);
//當(dāng)前線程為子線程
[self performSelectorOnMainThread:@selector(task_perform:) withObject:@"thread_name_04" waitUntilDone:YES];
}
- (void)task_perform:(NSObject *)obj{
NSLog(@"%@ - %@", obj, [NSThread currentThread]);
}

Snip20210323_27.png
- task方法中的執(zhí)行完打印之后,
子線程阻塞,當(dāng)主線程執(zhí)行完task_perform之后,子線程解除阻塞,然后繼續(xù)執(zhí)行,子線程任務(wù)執(zhí)行完,子線程銷毀; - 調(diào)用performSelectorOnMainThread方法,
若當(dāng)前線程是子線程,waitUntilDone = NO時,不會阻塞當(dāng)前子線程,子線程中任務(wù)繼續(xù)執(zhí)行,selector任務(wù)方法在主線程中執(zhí)行; - 將上面的代碼,waitUntilDone入?yún)⒏某蒒O,LLDB結(jié)果如下:

Snip20210323_28.png
- task方法中的執(zhí)行完打印之后,
子線程不會阻塞,繼續(xù)執(zhí)行,子線程任務(wù)執(zhí)行完成后直接銷毀; - 主線程執(zhí)行task_perform方法;
- 總結(jié):
- 若當(dāng)前線程為子線程,performSelectorOnMainThread:withObject:waitUntilDone方法中的參數(shù)waitUntilDone決定了是否阻塞當(dāng)前子線程,即同步/異步;
- 調(diào)用performSelectorInBackground方法,開啟新的線程在后臺執(zhí)行test方法任務(wù);
- 調(diào)用performSelector:onThread方法,在指定的線程執(zhí)行任務(wù);
NSThread常見方法和屬性
-
[NSThread currentThread]獲取當(dāng)前線程信息 -
NSThread sleepUntilDate:當(dāng)前線程休眠到指定時間 -
NSThread sleepForTimeInterval:當(dāng)前線程休眠多長時間 -
[NSThread exit]當(dāng)前線程退出 -
[NSThread mainThread]獲取主線程 -
[NSThread isMainThread]判斷當(dāng)前線程是否時主線程 -
[NSThread mainThread]獲取主線程 -
thread.isExecuting線程是否在執(zhí)行 -
thread.isCancelled線程是否被取消 -
thread.isFinished線程是否完成 -
thread.isMainThread線程是否是主線程 -
thread.threadPriority線程的優(yōu)先級,取值范圍0.0-1.0,默認(rèn)優(yōu)先級0.5,1.0表示最高優(yōu)先級,優(yōu)先級高,CPU調(diào)度的頻率高
GCD
dispatch_after
- 表示隊列中的block任務(wù)
延遲指定的時間執(zhí)行;
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"aaaaa");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"3333");
});
}
- 主隊列中的block任務(wù)延遲3秒再執(zhí)行;
dispatch_once
- 保證在App運行期間,block中的代碼只會執(zhí)行一次;
//創(chuàng)建單例
+ (instancetype)sharedInstance{
static dispatch_once_t onceToken;
static id instance;
dispatch_once(&onceToken, ^{
instance = [[self alloc]init];
});
return instance;
}
dispatch_apply
- 將耗時任務(wù),以指定的次數(shù),追加到隊列中,并等待任務(wù)全部執(zhí)行結(jié)束;
- 使用場景:for循環(huán)中有大量耗時任務(wù);
【第一種解決方案】:在for循環(huán)中耗時任務(wù),都異步開辟子線程去執(zhí)行
- (void)viewDidLoad {
[super viewDidLoad];
//在for循環(huán)中每一次任務(wù)都放到子線程中執(zhí)行
for (int i = 0; i < 1000; i++) {
dispatch_async(dispatch_queue_create(nil, DISPATCH_QUEUE_CONCURRENT), ^{
[NSThread sleepForTimeInterval:1]; // 模擬耗時操作
NSLog(@"%d -- %@", i, [NSThread currentThread]);
});
}
}
- 從打印結(jié)果來看,1000個耗時任務(wù)能很快執(zhí)行完,但是開辟了將近70個子線程來執(zhí)行耗時任務(wù),這樣會耗費大量的資源,容易導(dǎo)致App的崩潰,所以第一種方案不可?。?/li>
【第二種解決方案】:使用dispatch_apply函數(shù)
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_async(dispatch_queue_create(nil, DISPATCH_QUEUE_CONCURRENT), ^{
dispatch_apply(1000, dispatch_queue_create(nil, DISPATCH_QUEUE_CONCURRENT), ^(size_t index) {
[NSThread sleepForTimeInterval:1];//模擬耗時操作
NSLog(@"%zu -- %@", index, [NSThread currentThread]);
});
});
}
- 使用dispatch_apply函數(shù)將block任務(wù)循環(huán)1000次,加入并發(fā)隊列中;
- 從打印結(jié)果來看,1000個耗時任務(wù)執(zhí)行完成需要耗費很長一段時間,但是只開啟了五六個子線程循環(huán)執(zhí)行耗時任務(wù),不會耗費大量資源;
- 現(xiàn)在定義一個數(shù)組,然后使用dispatch_apply函數(shù)循環(huán)打印數(shù)組中元素,代碼實現(xiàn)如下所示:
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"-- begin --");
NSArray *arr = @[@"a", @"b", @"c", @"d", @"e"];
dispatch_queue_t queue = dispatch_queue_create("com.jarypan.gcdsummary", DISPATCH_QUEUE_CONCURRENT);
dispatch_apply(arr.count, queue, ^(size_t index) {
[NSThread sleepForTimeInterval:2];
NSLog(@"index = %zu, str = %@ -- %@", index, arr[index], [NSThread currentThread]);
});
NSLog(@"-- end --");
}
- LLDB調(diào)試結(jié)果:

Snip20210324_29.png
- 使用dispatch_apply函數(shù)往并發(fā)隊列中循環(huán)添加了5個任務(wù);
- 開啟多個子線程去執(zhí)行任務(wù),
在執(zhí)行任務(wù)時會阻塞主線程,只有當(dāng)dispatch_apply添加到并發(fā)隊列中的任務(wù)全部執(zhí)行完,才會解除主線程的阻塞,主線程才能繼續(xù)執(zhí)行; - 為了不阻塞主線程的執(zhí)行,我們將dispatch_apply函數(shù)放到異步子線程中去執(zhí)行,代碼改造之后如下:
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"-- begin --");
NSArray *arr = @[@"a", @"b", @"c", @"d", @"e"];
dispatch_queue_t queue = dispatch_queue_create("com.jarypan.gcdsummary", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"current thread -- %@", [NSThread currentThread]);
dispatch_apply(arr.count, queue, ^(size_t index) {
[NSThread sleepForTimeInterval:2];
NSLog(@"index = %zu, str = %@ -- %@", index, arr[index], [NSThread currentThread]);
});
NSLog(@" current thread - end ");
});
NSLog(@"-- end --");
}
- LLDB調(diào)試結(jié)果:

Snip20210324_30.png
- 可以看到dispatch_apply函數(shù)會
阻塞當(dāng)前子線程的繼續(xù)執(zhí)行,當(dāng)追加到并發(fā)隊列中任務(wù)執(zhí)行完成,才會解除子線程的阻塞 - 任務(wù)循環(huán)添加到并發(fā)隊列,但是任務(wù)執(zhí)行卻是串行執(zhí)行,并沒有實現(xiàn)我們想要的并發(fā)執(zhí)行,(為什么?),如果
將上面的自定義并發(fā)隊列改成全局隊列就可以實現(xiàn)任務(wù)的并發(fā)執(zhí)行;
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"-- begin --");
NSArray *arr = @[@"a", @"b", @"c", @"d", @"e"];
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
NSLog(@"current thread -- %@", [NSThread currentThread]);
dispatch_apply(arr.count, queue, ^(size_t index) {
[NSThread sleepForTimeInterval:2];
NSLog(@"index = %zu, str = %@ -- %@", index, arr[index], [NSThread currentThread]);
});
NSLog(@" current thread - end ");
});
NSLog(@"-- end --");
}
- LLDB調(diào)試結(jié)果:

Snip20210324_31.png
dispatch_apply會造成死鎖的場景
第一種:在主線程使用dispatch_apply往主隊列中添加任務(wù)
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_apply(10, dispatch_get_main_queue(), ^(size_t index) {
NSLog(@"%zu", index);
});
}
-
dispatch_apply往主隊列中添加block任務(wù),而viewDidLoad已在主線中執(zhí)行,block任務(wù)會等待viewDidLoad執(zhí)行完再執(zhí)行; - 由于
dispatch_apply會阻塞主線程的繼續(xù)執(zhí)行,viewDidLoad會等待block任務(wù)執(zhí)行完才會繼續(xù)執(zhí)行完成;造成viewDidLoad與block任務(wù)的相互等待形成死鎖; -
dispatch_apply可以看成dispatch_sync;
第二種情況:使用dispatch_apply往異步串行隊列中追加任務(wù)
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_queue_t queue = dispatch_queue_create(nil, DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{//block1
dispatch_apply(10, queue, ^(size_t index) {//block2
NSLog(@"%zu", index);
});
});
}
- 將
dispatch_apply看成dispatch_sync,具體原因在 iOS底層系列23 -- 多線程的函數(shù)與隊列中的案例分析已經(jīng)詳細(xì)分析過了;
dispatch_group調(diào)度組
-
dispatch_group_async,dispatch_group_enter,dispatch_group_leave,dispatch_group_notify與dispatch_group_wait配合使用; - 使用場景:多個異步網(wǎng)絡(luò)請求,回調(diào)統(tǒng)一處理;
- (void)viewDidLoad {
[super viewDidLoad];
self.bookId = @"11186718404761703yw";
//創(chuàng)建隊列組
self.group = dispatch_group_create();
//創(chuàng)建隊列
self.queue = dispatch_get_global_queue(0, 0);
dispatch_group_async(self.group, self.queue, ^{
[self loadBookView];
});
dispatch_group_async(self.group, self.queue, ^{
[self loadBookEvalute];
});
dispatch_group_async(self.group, self.queue, ^{
[self loadBookRecommandList];
});
long timeout = dispatch_group_wait(self.group, dispatch_time(DISPATCH_TIME_NOW, 1 *NSEC_PER_SEC));
NSLog(@"timeout = %ld", timeout);
if (timeout == 0) {
NSLog(@"按時完成任務(wù)");
}else{
NSLog(@"超時");
}
dispatch_group_notify(self.group, self.queue, ^{
NSLog(@"同步所有異步請求");
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"刷新UI");
});
});
}
- (void)loadBookView{
///請求一
dispatch_group_enter(self.group);
[YYRequestBookApi requestBookViewWithBookId:self.bookId success:^(id _Nonnull response) {
dispatch_group_leave(self.group);
} fail:^(NSString * _Nonnull error) {
dispatch_group_leave(self.group);
}];
}
- (void)loadBookEvalute{
///請求二
dispatch_group_enter(self.group);
[YYRequestBookApi requestBookEvaluteWithBookId:self.bookId success:^(id _Nonnull response) {
dispatch_group_leave(self.group);
} fail:^(NSString * _Nonnull error) {
dispatch_group_leave(self.group);
}];
}
- (void)loadBookRecommandList{
///請求三
dispatch_group_enter(self.group);
[YYRequestBookApi requestBookRecommentWithBookId:self.bookId success:^(id _Nonnull response) {
dispatch_group_leave(self.group);
} fail:^(NSString * _Nonnull error) {
dispatch_group_leave(self.group);
}];
}
-
dispatch_group_enter與dispatch_group_leave需要配對使用; -
dispatch_group_wait設(shè)置調(diào)度組等待的超時時間(即等多久); - 當(dāng)三個網(wǎng)絡(luò)請求的回調(diào)數(shù)據(jù)都回來之后,在
dispatch_group_notify函數(shù)中做統(tǒng)一處理;
dispatch_barrier柵欄函數(shù)
- 可實現(xiàn)并發(fā)隊列中的任務(wù),按照順序執(zhí)行;
- dispatch_barrier分為兩種分別為
dispatch_barrier_sync與dispatch_barrier_async
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_queue_t queue = dispatch_queue_create("com.lyy.queue", DISPATCH_QUEUE_CONCURRENT);
NSLog(@" start ");
dispatch_async(queue, ^{
sleep(5);
NSLog(@"---1--- %@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"---2--- %@",[NSThread currentThread]);
});
dispatch_barrier_sync(queue, ^{
NSLog(@" 柵欄函數(shù) -- %@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"---3--- %@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"---4--- %@",[NSThread currentThread]);
});
NSLog(@" end ");
}
- 并發(fā)隊列中追加四個任務(wù),任務(wù)1是耗時任務(wù),子線程休眠5秒;
dispatch_barrier_sync柵欄同步添加在四個任務(wù)的正中間;

Snip20210324_32.png
- 可以看出
dispatch_barrier_sync會阻塞主線程的繼續(xù)執(zhí)行,當(dāng)其block執(zhí)行完成時,主線程解除阻塞繼續(xù)執(zhí)行; -
dispatch_barrier_sync柵欄同步將并發(fā)隊列的任務(wù)執(zhí)行分割開,必須等任務(wù)1(耗時任務(wù))與任務(wù)2都執(zhí)行完畢時才會執(zhí)行柵欄同步函數(shù),柵欄同步函數(shù)執(zhí)行完成之后再執(zhí)行任務(wù)3,任務(wù)4。 - 將上面的
dispatch_barrier_sync柵欄同步改成dispatch_barrier_async柵欄異步,調(diào)試結(jié)果如下:

Snip20210324_34.png
- 可以看出
dispatch_barrier_async柵欄異步不會阻塞主線程,end會立即執(zhí)行且會開辟子線程,在子線程中執(zhí)行; -
dispatch_barrier_async柵欄異步將并發(fā)隊列的任務(wù)執(zhí)行分割開,必須等任務(wù)1(耗時任務(wù))與任務(wù)2都執(zhí)行完畢時才會執(zhí)行柵欄異步函數(shù),柵欄異步函數(shù)執(zhí)行完成之后再執(zhí)行任務(wù)3,任務(wù)4。
dispatch_semaphore_t信號量
- 主要涉及三個函數(shù)如下:
-
dispatch_semaphore_create()創(chuàng)建信號量,并設(shè)定一個初始信號量值; -
dispatch_semaphore_signal()發(fā)送信號量,信號量值+1; -
dispatch_semaphore_wait()等待信號量,信號量值-1;
-
-
當(dāng)信號量值 < 0時,會阻塞當(dāng)前線程的繼續(xù)執(zhí)行,當(dāng)信號量>=0時,當(dāng)前線程解除阻塞,會繼續(xù)執(zhí)行接下來的邏輯;
應(yīng)用場景一:配合dispatch_group實現(xiàn)多個異步網(wǎng)絡(luò)請求,回調(diào)統(tǒng)一處理,實現(xiàn)如下:
- (void)viewDidLoad {
[super viewDidLoad];
self.bookId = @"11186718404761703yw";
self.semphore = dispatch_semaphore_create(0);
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_group_async(group, queue, ^{
[self loadBookView];
});
dispatch_group_async(group, queue, ^{
[self loadBookEvalute];
});
dispatch_group_async(group, queue, ^{
[self loadBookRecommandList];
});
dispatch_group_notify(group, queue, ^{
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"刷新UI");
});
});
}
- (void)loadBookView{
///請求一
[YYRequestBookApi requestBookViewWithBookId:self.bookId success:^(id _Nonnull response) {
dispatch_semaphore_signal(self.semphore);
} fail:^(NSString * _Nonnull error) {
dispatch_semaphore_signal(self.semphore);
}];
dispatch_semaphore_wait(self.semphore, DISPATCH_TIME_FOREVER);
}
- (void)loadBookEvalute{
///請求二
[YYRequestBookApi requestBookEvaluteWithBookId:self.bookId success:^(id _Nonnull response) {
dispatch_semaphore_signal(self.semphore);
} fail:^(NSString * _Nonnull error) {
dispatch_semaphore_signal(self.semphore);
}];
dispatch_semaphore_wait(self.semphore, DISPATCH_TIME_FOREVER);
}
- (void)loadBookRecommandList{
///請求三
[YYRequestBookApi requestBookRecommentWithBookId:self.bookId success:^(id _Nonnull response) {
dispatch_semaphore_signal(self.semphore);
} fail:^(NSString * _Nonnull error) {
dispatch_semaphore_signal(self.semphore);
}];
dispatch_semaphore_wait(self.semphore, DISPATCH_TIME_FOREVER);
}
應(yīng)用場景二:實現(xiàn)異步并發(fā)(串行)隊列中的任務(wù),依次順序執(zhí)行,如果是隊列中的任務(wù)是網(wǎng)絡(luò)請求,也是可以控制網(wǎng)絡(luò)請求回調(diào)的順序執(zhí)行
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
sleep(1);
NSLog(@"任務(wù)1:%@",[NSThread currentThread]);
dispatch_semaphore_signal(sem);//2
});
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);//1
dispatch_async(dispatch_get_global_queue(0, 0), ^{
sleep(1);
NSLog(@"任務(wù)2:%@",[NSThread currentThread]);
dispatch_semaphore_signal(sem);//4
});
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);//3
dispatch_async(dispatch_get_global_queue(0, 0), ^{//5
sleep(1);
NSLog(@"任務(wù)3:%@",[NSThread currentThread]);
});
}

Snip20210324_35.png
- 信號量的初始值為0,首先執(zhí)行到1,等待信號量信號量減1,變成-1會阻塞當(dāng)前主線程的繼續(xù)執(zhí)行,所以dispatch_semaphore_wait后面的代碼執(zhí)行不了;
- 第一個
dispatch_async函數(shù)開始執(zhí)行,當(dāng)執(zhí)行到2時,發(fā)送信號量,信號量+1變成0,那么主線程解除阻塞繼續(xù)執(zhí)行,來到3等待信號量信號量減1,又變成-1,又會阻塞當(dāng)前主線程,后面的代碼不能執(zhí)行; - 第二個
dispatch_async函數(shù)開始執(zhí)行,當(dāng)執(zhí)行到4時,發(fā)送信號量,信號量+1變成0,那么主線程解除阻塞繼續(xù)執(zhí)行,執(zhí)行第三個dispatch_async函數(shù);
應(yīng)用場景三:控制異步并發(fā)的數(shù)量
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_semaphore_t semaphore = dispatch_semaphore_create(5);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (int i = 0; i < 20; i++) {
dispatch_async(queue, ^{
//等待降低信號量
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"run task %d -- %@",i,[NSThread currentThread]);
sleep(2);
NSLog(@"complete task %d -- %@",i,[NSThread currentThread]);
//提高信號量
dispatch_semaphore_signal(semaphore);
});
}
}
- 當(dāng)信號量初始值設(shè)置為3時,LLDB調(diào)試如下:

Snip20210324_36.png
- 當(dāng)信號量初始值設(shè)置為5時,LLDB調(diào)試如下:

Snip20210324_37.png
- 可以看到信號量的初始值,控制了異步任務(wù)的并發(fā)數(shù),即同一時刻任務(wù)執(zhí)行的數(shù)量;
dispatch_source_t
-
dispatch_source_t主要用于計時操作,計時精準(zhǔn)度比NSTimer高,其原因是因為它創(chuàng)建的timer不依賴于RunLoop; - 常見API方法如下:
- dispatch_source_create: 創(chuàng)建事件源
- dispatch_source_set_event_handler: 設(shè)置數(shù)據(jù)源回調(diào)
- dispatch_source_merge_data: 設(shè)置事件源數(shù)據(jù)
- dispatch_source_get_data: 獲取事件源數(shù)據(jù)
- dispatch_resume: 繼續(xù)
- dispatch_suspend: 掛起
- dispatch_cancle: 取消
- (void)viewDidLoad {
[super viewDidLoad];
//1.創(chuàng)建隊列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//2.創(chuàng)建timer
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
self.timer = timer;
//3.設(shè)置timer首次執(zhí)行時間,間隔,精確度
dispatch_source_set_timer(self.timer, DISPATCH_TIME_NOW, 2.0*NSEC_PER_SEC, 0);
//4.設(shè)置timer事件回調(diào)
dispatch_source_set_event_handler(self.timer, ^{
NSLog(@"GCDTimer");
});
//5.默認(rèn)是掛起狀態(tài),需要手動激活
dispatch_resume(self.timer);
}
NSOperation
- NSOperation是基于GCD之上的更高一層封裝,NSOperation需要配合NSOperationQueue來實現(xiàn)多線程;
NSInvocationOperation與NSBlockOperation
-
NSOperation是抽象類,在實際使用的中需要使用它的兩個子類NSInvocationOperation與NSBlockOperation;
- (void)viewDidLoad {
[super viewDidLoad];
//創(chuàng)建NSInvocationOperation實例對象 并綁定操作方法
NSInvocationOperation *operation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(task) object:nil];
//start開始執(zhí)行操作
[operation start];
//創(chuàng)建NSBlockOperation實例對象 在Block中添加任務(wù)操作
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
[NSThread sleepForTimeInterval:1];//模擬耗時操作
NSLog(@"NSBlockOperation excute - %@", [NSThread currentThread]);
}];
//start開始執(zhí)行操作
[operation1 start];
}
- (void)task{
NSLog(@"NSInvocationOperation excute - %@",[NSThread currentThread]);
}
- LLDB調(diào)試結(jié)果:

Snip20210324_38.png
- 單獨使用NSInvocationOperation與NSBlockOperation時,在主線程中執(zhí)行任務(wù)操作;
- NSBlockOperation還可以調(diào)用
addExecutionBlock函數(shù),給NSBlockOperation添加其他block任務(wù),這些任務(wù)在子線程中并發(fā)執(zhí)行;
- (void)viewDidLoad {
[super viewDidLoad];
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
[NSThread sleepForTimeInterval:2];
NSLog(@"1---%@", [NSThread currentThread]);
}];
[operation addExecutionBlock:^{
[NSThread sleepForTimeInterval:2];
NSLog(@"2---%@", [NSThread currentThread]);
}];
[operation addExecutionBlock:^{
[NSThread sleepForTimeInterval:2];
NSLog(@"3---%@", [NSThread currentThread]);
}];
[operation start];
}
- LLDB調(diào)試結(jié)果:

Snip20210324_39.png
自定義NSOperation
#import <Foundation/Foundation.h>
@interface YYOperation : NSOperation
@end
#import "YYOperation.h"
@implementation YYOperation
//重寫main方法
- (void)main{
if (!self.isCancelled) {
[NSThread sleepForTimeInterval:2];
NSLog(@"main --- %@", [NSThread currentThread]);
}
}
@end
- (void)viewDidLoad {
[super viewDidLoad];
YYOperation *operation = [[YYOperation alloc]init];
[operation start];
}
- main函數(shù)中的任務(wù)操作在主線程中執(zhí)行;
NSOperationQueue的使用
- 其使用步驟如下:
- 1、創(chuàng)建任務(wù):先將需要執(zhí)行的任務(wù)封裝到NSOperation對象中;
- 2、創(chuàng)建隊列:創(chuàng)建NSOperationQueue;
- 3、將任務(wù)加入到隊列中:將NSOperation操作對象添加到NSOperationQueue中;
- (void)viewDidLoad {
[super viewDidLoad];
//1.獲取主隊列
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
//2.創(chuàng)建操作
//使用 NSInvocationOperation 創(chuàng)建操作1
NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task1) object:nil];
//使用 NSInvocationOperation 創(chuàng)建操作2
NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task2) object:nil];
//使用 NSBlockOperation 創(chuàng)建操作3
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
[NSThread sleepForTimeInterval:2];
NSLog(@"3---%@", [NSThread currentThread]);
}];
[op3 addExecutionBlock:^{
[NSThread sleepForTimeInterval:2];
NSLog(@"4---%@", [NSThread currentThread]);
}];
//3.調(diào)用addOperation: 添加所有操作到主隊列中
[mainQueue addOperation:op1]; //[op1 start]
[mainQueue addOperation:op2]; //[op2 start]
[mainQueue addOperation:op3]; //[op3 start]
}
- (void)task1{
[NSThread sleepForTimeInterval:2];
NSLog(@"1---%@", [NSThread currentThread]);
}
- (void)task2{
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
NSLog(@"2---%@", [NSThread currentThread]); // 打印當(dāng)前線程
}
- LLDB調(diào)試結(jié)果:

Snip20210324_42.png
- 創(chuàng)建的操作添加到NSOperationQueue中,
不用再調(diào)用start方法來啟動執(zhí)行,是由系統(tǒng)去啟動執(zhí)行操作; - 添加到主隊列中的操作都是在主線程中,依次執(zhí)行,由于op3調(diào)用addExecutionBlock添加的操作會并發(fā)執(zhí)行;
- 將上面的隊列改成自定義隊列 NSOperationQueue *queue = [[NSOperationQueue alloc]init],LLDB調(diào)試如下:

Snip20210324_43.png
- 可以看到操作任務(wù)添加到自定義隊列中,是在子線程中并發(fā)執(zhí)行;
往NSOperationQueue中直接添加block任務(wù)
- (void)viewDidLoad {
[super viewDidLoad];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
for (int i = 0; i < 5; i++) {
[queue addOperationWithBlock:^{
NSLog(@"%@---%d", [NSThread currentThread], i);
}];
}
}
- LLDB調(diào)試結(jié)果如下:

Snip20210324_44.png
- NSOperationQueue調(diào)用
addOperationWithBlock直接往操作隊列中追加block任務(wù); - 由于添加到自定義操作隊列,所以所有任務(wù)并發(fā)執(zhí)行;
往NSOperationQueue設(shè)置并發(fā)數(shù)
- 在GCD中我們通過信號量
dispatch_semaphore_t,控制異步并發(fā)隊列的并發(fā)數(shù); - 在NSOperationQueue中我們直接通過其屬性
maxConcurrentOperationCount,控制自定義NSOperationQueue操作隊列的并發(fā)數(shù);
- (void)viewDidLoad {
[super viewDidLoad];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.name = @"lyy.queue";
queue.maxConcurrentOperationCount = 2;
for (int i = 0; i < 10; i++) {
[queue addOperationWithBlock:^{ // 一個任務(wù)
[NSThread sleepForTimeInterval:2];
NSLog(@"%d-%@",i,[NSThread currentThread]);
}];
}
}
- LLDB調(diào)試結(jié)果如下:

Snip20210324_45.png
- 當(dāng)自定義操作隊列的最大并發(fā)數(shù)設(shè)置為2時,表明同一時刻只開辟兩個子線程執(zhí)行任務(wù);當(dāng)最大并發(fā)數(shù)設(shè)置為3時,LLDB調(diào)試結(jié)果如下:

Snip20210324_46.png
NSOperation設(shè)置依賴
- 任務(wù)操作之間添加依賴,代碼實現(xiàn)如下:
- (void)viewDidLoad {
[super viewDidLoad];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
[NSThread sleepForTimeInterval:0.5];
NSLog(@"請求token");
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
[NSThread sleepForTimeInterval:0.5];
NSLog(@"拿著token,請求數(shù)據(jù)1");
}];
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
[NSThread sleepForTimeInterval:0.5];
NSLog(@"拿著數(shù)據(jù)1,請求數(shù)據(jù)2");
}];
[op2 addDependency:op1];
[op3 addDependency:op2];
[queue addOperations:@[op1,op2,op3] waitUntilFinished:YES];
NSLog(@"執(zhí)行完了 -- 我要干其他事");
}
- LLDB調(diào)試結(jié)果如下:

Snip20210324_47.png
- op2依賴于op1,表明只有先執(zhí)行op1,才能執(zhí)行op2;
- op3依賴于op2,表明只有先執(zhí)行op2,才能執(zhí)行op3;
NSOperation設(shè)置優(yōu)先級
- NSOperation設(shè)置優(yōu)先級只會讓CPU有更高的幾率調(diào)用,不是說設(shè)置高就一定全部先執(zhí)行;
- 常見優(yōu)先級有如下:從高到低
- NSQualityOfServiceUserInteractive:用戶交互級別:最高級別,通常用于響應(yīng)用戶操作的UI處理,如將圖像繪制到屏幕上;
- NSQualityOfServiceUserInitiated:用戶發(fā)起級別:由用戶發(fā)起的僅次于UI的任務(wù),用戶希望立即響應(yīng)并在此任務(wù)完成后進(jìn)行下一步操作。如遠(yuǎn)程內(nèi)容載入;
- NSQualityOfServiceUtility:工具級別:由用戶或自動發(fā)起,不必要立即響應(yīng),不阻止用戶交互。通常以一個可見的進(jìn)度條來標(biāo)示進(jìn)度,例如預(yù)加載內(nèi)容、上傳或大量文件操作(如媒體導(dǎo)入);
- NSQualityOfServiceBackground:后臺級別:通常不是由用戶發(fā)起、用戶不可見的。例如備份、索引、數(shù)據(jù)同步等;
- NSQualityOfServiceDefault:將從其他來源推測QoS,如果無法推斷,將使用 UserInitiated 到 Utility中的一個;
- (void)viewDidLoad {
[super viewDidLoad];
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 3; i++) {
// sleep(1);
NSLog(@"第一個操作 %d --- %@", i, [NSThread currentThread]);
}
}];
//設(shè)置最低優(yōu)先級
op1.qualityOfService = NSQualityOfServiceBackground;
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 3; i++) {
NSLog(@"第二個操作 %d --- %@", i, [NSThread currentThread]);
}
}];
//設(shè)置最高優(yōu)先級
op2.qualityOfService = NSQualityOfServiceUserInteractive;
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:op1];
[queue addOperation:op2];
}
- LLDB調(diào)試結(jié)果如下:

Snip20210324_50.png
- 由于op2的優(yōu)先級比op1優(yōu)先級高,所以先執(zhí)行op2;
- 若在op1中加入睡眠一秒的操作,且設(shè)置op1的優(yōu)先級最高,op2優(yōu)先級最低,代碼如下所示:
- (void)viewDidLoad {
[super viewDidLoad];
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 3; i++) {
sleep(1);
NSLog(@"第一個操作 %d --- %@", i, [NSThread currentThread]);
}
}];
//設(shè)置最高優(yōu)先級
op1.qualityOfService = NSQualityOfServiceUserInteractive;
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 3; i++) {
NSLog(@"第二個操作 %d --- %@", i, [NSThread currentThread]);
}
}];
//設(shè)置最低優(yōu)先級
op2.qualityOfService = NSQualityOfServiceBackground;
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:op1];
[queue addOperation:op2];
}
- LLDB調(diào)試結(jié)果如下:

Snip20210324_51.png
- 看到即使op1的優(yōu)先級最高,op2優(yōu)先級最低,由于op1有阻塞操作,最終op2首先執(zhí)行,表明NSOperation設(shè)置優(yōu)先級只會讓CPU有更高的幾率調(diào)用,不是說設(shè)置高就一定先執(zhí)行,還要考慮其他因素;
NSOperationQueue實現(xiàn)線程間通信
- 代碼實現(xiàn)如下:
- (void)viewDidLoad {
[super viewDidLoad];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.name = @"lyy.queue";
[queue addOperationWithBlock:^{
//子線程執(zhí)行耗時操作
NSLog(@"耗時操作 -- %@", [NSThread currentThread]);
//切換到主線程刷新UI
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
NSLog(@"刷新UI -- %@", [NSThread currentThread]);
}];
}];
}
- LLDB調(diào)試結(jié)果如下:

Snip20210324_49.png