一、線程概述
1、 iOS裡面的線程按種類可分為同步線程和異步線程。同步線程指調用同步線程的地方必須等到同步線程執行完畢才可以繼續向下執行。而調用異步線程的地方則在執行完調用異步線程的語句後就可以繼續向下執行。
2、線程按調用方式又可以大致分為以下幾種類型:NSObject、NSThread、NSOperation和GCD。NSObject和NSThread只能管理單個的線程,功能較簡單,GCD和NSOperation則可以進行隊列等復雜操作,且效率較高。其中GCD方式最為有效,NSOperation是基於GCD封裝的,功能相對來說更豐富。
3、異步線程中往往要創建自己的自動釋放池來釋放異步線程中創建的自動釋放對象。
4、異步線程中有需要的話,要做自己的異常捕捉處理,因為當異步線程中產生異常時,主線程很有可能會捕捉不到異步線程拋出的異常。
二、線程調用
1、NSObject
* 線程創建
NSObjcet對象自身實現了performSelectorInBackground、performSelectorOnMainThread和performSelector:onThread:withObject:等線程相關方法。
1.1 performSelectorInBackground是創建一個異步線程
-(void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait :
aSelector是在主線程中執行的方法,arg是傳遞的參數,wait指是否要等待aSelector執行完畢。
1.2 performSelectorOnMainThread是返回主線程執行相關方法
-(void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait:
aSelector是在主線程中執行的方法,arg是傳遞的參數,wait指是否要等待aSelector執行完畢。
1.3 performSelector:onThread:withObject:是執行某個特定線程中的方法
-(void)performSelector:(SEL)aSelector onThread:(NSThread *) withObject:(id)arg waitUntilDone:(BOOL)wait:
該方法一般用於線程間相互通信,即在一個線程中發送消息給另一個線程
注:每一個線程都有自己的RunLoop,但非主線程的RunLoop默認是關閉的,當需要進行非主線程的通信時,需要確保通信線程的RunLoop是開啟的,否則發送給通信線程的消息不會被執行。
1.4 -(void)performSelector:withObject:afterDelay:是指在當前線程延遲執行某個方法
* 線程的暫停、繼續和取消
NSObject是無法暫停和取消線程的。
2、NSThread
* 線程創建
NSThread有兩種創建線程的方式: detachNewThreadSelector和initWithTarget.
2.1 detachNewThreadSelector:toTarget: withObject:
調用這種方法創建的線程會主動運行,不需要手動啟動。
2.2 initWithTarget: selector:objcet:
調用這種方式新建一個線程,新建完成後需要調用NSThread的start方法來啟動線程。
* 線程的取消
調用NSThrea的cancel方法即可取消線程,但正在執行的線程不會馬上結束。所以NSThread的cancel方法的實質意義是把線程的狀態設置為已取消,然後編代碼時就可以依賴線程的狀態去結束掉線程。
* 線程的暫停和繼續
NSThread沒有提供暫停的方法
3、NSOperation
* 線程創建
NSOperation通常和NSOperationQueue配對使用,創建好NSOperation對象後添加到NSOperationQueue中,NSOperationQueue即可為我們管理NSOperation,包括為NSOperation配置異步線程和啟動等。
NSOperationQueue按類型分為:主隊列和自定義隊列。主隊列運行在主線程上,而自定義隊列在後台執行。主隊列獲取方式:[NSOperationQueue mainQueue];自定義隊列獲取方式:[[NSOperationQueue alloc] init]。
NSOperation按類型分為:NSOperation、NSBlockOperation和NSInvocationOperation。
3.1 NSOperation:
NSOperation是一個虛類,我們需要繼續NSOperation來創建子類,並重寫main方法或start方法。重寫main方法很簡單,只需要重寫好main方法,不需要管理一些狀態屬性(如isExecuted和isFinished),當main方法返回就假定操作結束了;而重寫start方法則更靈活,擁有更多的控制權,但是狀態屬性信息則需要自己控制。
3.2 NSBlockOperation:
NSBlockOperation是調用blockOperationWithBlock方法以一個block作為參數創建的一個operation對象,並且在對象實例化後還可以調用addExecutionBlock方法動態添加block,但添加動作需要在operation執行前發生,通常也就是在operation添加到NSOperationQueue前,否則很可能產生異常和莫名錯誤。NSBlockOperation的業務邏輯操作主要寫在block中。
3.3 NSInvocationOperation:
NSInvocationOperation是以一個SEL方法或NSInvocation為參數初始化的operation對象。NSInvocationOperation的業務邏輯操作主要放在對應的SEL方法或NSInvocation對象中。
* 線程的暫定和繼續
調用NSOperationQueue對象的setSuspended:YES即可暫停NSOperationQueue中待執行線程的執行,然後調用setSuspended:NO即可繼續執行。
* 線程的取消
調用NSOperation對象的cancel方法會取消單個NSOperation對象的執行,而調用NSOperationQueue對象的cancelAllOperation方法會取消NSOperation隊列中所有對象的執行。
* 線程依賴
如果NSOperation對象有執行順利要求的話,比如operationB需要在operationA執行完畢後才可以執行,那就可以通過設置NSOperation之間的依賴關系來實現:[operationB addDependency:operationA]。
* 手動執行
如果NSOperation不和NSOperationQueue配對使用,這時就需要調用NSOperation的start方法來執行NSOperation,通常要重寫isExecuting,isFinished,start和main等方法,如果要實現並發操作,則還要重寫isConcurrent方法,如果NSOperation間有依賴關系,則還要重寫isReady方法。示例如下:
@interface HandOperation(){
BOOL executing; // 是否正在執行
BOOL finished;// 是否執行結束
}
-(void)completeOperation;// 結束後的狀態處理方法
@end
@implementation HandOperation
#pragma mark - Lifecycle Methods
-(id)init{
self = [super init];
if(self){
executing = NO;
finished = NO;
}
return self;
}
-(BOOL)isConcurrent{
return YES;
}
-(BOOL)isExecuting{
return executing;
}
-(BOOL)isFinished{
return finished;
}
-(BOOL)isReady{
return [super isReady];
}
-(void)start{
if([self isCancelled]){
[self willChangeValueForKey:@“isFinished”];
finished = YES;
[self didChangeValueForKey:@“isFinished”];
return;
}
[self willChangeValueForKey:@“isExecuting”];
executing = YES;
[NSThread detachNewThreadSelector:@selector(main) toTarget:self withObject:nil];
[self didChangeValueForKey:@“isExecuting”];
}
-(void)main{
@try{
@autoraleasepool{
[self handleForTask];
}
[self completeOperation];
}
@catch(NSException *exception){
}
@finally{
}
}
#pragma mark - Custom Methods
-(void)completeOperation{
[self willChangeValueForKey:@“isFinished”];
[self willChangeValueForKey:@“isExecuting”];
executing = NO;
finished = YES;
[self didChangeValueForKey:@“isExecuting”];
[self didChangeValueForKey:@“isFinished”];
}
//業務處理方法
-(void)handleForTask{
for(int i=0;i<10;i++){
if(![self isCancelled]){
NSLog(@“%s %i”,__func__,i);
sleep(1);
}
}
}
4、GCD
* 線程創建
GCD是Grand Central Dispatch的縮寫,是Apple開發的一個多核編程的解決方案。GCD通常是把block以dispatch_async或dispatch_sync的方式添加到dispatch_queue_t隊列中來創建線程: dispatch_async(dispatch_queue_t queue ,dispatch_block block)。
dispatch_async為異步添加,等dispatch_async語句執行完畢後即可繼續執行下去;而dispatch_sync為同步添加,需要等dispatch_sync添加的block內容執行完畢後才可以繼續向下執行。
dispatch_queue_t分為三種類型: Serial Queues、Main Queues和Concurrent Queues。
4.1 Serial Queues
Serial Queues稱為串行隊列,Serial Queues隊列中的任務只能順序執行,一次執行一個。我們可以在Serial Queues中執行有依賴關系的任務或同步任務。
Serial Queues隊列之間是可以並發執行的。
Serial Queues是通過dispatch_queue_create(“testQueue”,NULL)語句創建的,“testQueue”是隊列標識符,NULL對應一個表示隊列屬性的參數,只要傳入NULL或DISPATCH_QUEUE_SERIAL即可獲取一個串行隊列。
4.2 Main Queues
Main Queues 稱為主線程隊列,所有提交至Main Queues中的任務都會在主線程中執行,所以Main Queues也是一個串行隊列。
Main Queues可以通過dispatch_get_main_queue()語句獲取到,Main Queues是全局性的隊列。
4.3 Concurrent Queues
Concurrent Queues稱為並行隊列,所有提交至Concurrent Queues中的任務都是並行執行的。Concurrent Queues又分為Global Queues和User Concurrent Queues。Global Queues由系統根據優先級提供四個不同的dispatch queue。
Global Queues可以通過dispatch_get_global_queue(dispatch_queue_priority_t priority,unsigned long flags)語句獲取到,priority是優先級,優先級有DISPATCH_QUEUE_PRIORITY_HIGH、DISPATCH_QUEUE_PRIORITY_DEFAULT、DISPATCH_QUEUE_PRIORITY_LOW、DISPATCH_QUEUE_PRIORITY_BACKGROUND四種:flags是保留字段,現階段傳入0即可。
User Concurrent Queues是通過dispatch_queue_create(“testQueue”,DISPATCH_QUEUE_CONCURRENT)語句創建的,“testQueue”是隊列標識符,DISPATCH_QUEUE_CONCURRENT表示隊列為並行隊列。
* 線程的暫停和繼續
調用dispatch_suspend(dispatch_queue_t)即可掛起隊列,使隊列中待執行的任務暫停執行,但正在執行的任務還是會繼續執行。
調用dispatch_resume(dispatch_queue_t)即可以使掛起的隊列繼續執行。
dispatch_suspend 和 dispatch_resume只對Serial Queues 和 User Concurrent Queues有效,因為Main Queues 和Global Queues都是全局隊列。
* 線程的取消
GCD中沒有顯式的線程取消方法調用,只能在代碼中根據預先設置的標志位去實現取消操作。
* dispatch_group_async的使用
dispatch_group_async可以實現監聽一組任務是否完成,完成後得到通知執行其他操作。
使用dispatch_group_async添加一組任務後,可以使用dispatch_group_wait方法同步等待全部任務執行完畢,然後才執行之後的其他任務;也可以使用dispatch_group_notify異步監聽前面的任務,等前面任務執行完畢後觸發dispatch_group_notify中的處理事件。如下:
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group,globalQueue,^{
@autoreleasepool{
for(int i=0;i<5;i++){
NSLog(@“001 %i”,i);
sleep(1);
}
}
});
dispatch_group_async(group,globalQueue,^{
@autoreleasepool{
for(int i=0;i<5;i++){
NSLog(@“002 %i”,i);
sleep(1);
}
}
});
dispatch_group_wait(group,DISPATCH_TIME_FOREVER);
/*
dispatch_group_notify(group,globalQueue,^{
@qutoreleasepool{
NSLog(@“notify End”);
}
});
*/
dispatch_release(group);
* dispatch_barrier_async的使用
dispatch_barrier_async是在前面的任務執行結束後它才執行,而且它後面的任務等它執行完成後才執行。
dispatch_barrier_async(dispatch_queue_t queue,dispatch_block_t block),queue必須是自定義的並行隊列,如果是其他隊列,那麼dispatch_barrier_async的效果和dispatch_sync是相同的。
* dispatch_apply 的使用
dispatch_apply可以執行某個代碼片段N次,可以用在相互間沒依賴關系的並行計算等。代碼如下:
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_PRIORITY_DEFAULT,0);
__block int sum = 0;
dispatch_apply(10,globalQueue,^(size_t i){
sum += i;
});