在IOS開發中為提高程序的運行效率會將比較耗時的操作放在子線程中執行,iOS系統進程默認啟動一個主線程,用來響應用戶的手勢操作以及UI刷新,因此主線程又叫做UI線程。
前面的Blog說明了NSThread以及GCD處理并發線程以及線程安全(線程鎖@synchronized(){}),線程通信(從子線程向主線程傳遞值,刷新界面),接下來討論線程的延遲與執行的單一化(唯一執行一次)。
1.延遲
(1)最簡單粗暴的方法是對線程本身執行掛起操作
[NSThread sleepForTimeInterval:]
另外使用隱式創建開啟線程的方法
self performSelector:withObject:afterDelay:
(2)使用GCD處理延遲操作(線程,任務)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(<#delayInSeconds#> * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
<#code to be executed after a specified delay#>
});
2.只執行一次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
<#code to be executed once#>
});
3.為了方便管理任務隊列中的任務,GCD特用分組的方式來管理
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_async(group, queue, ^{
});
dispatch_group_notify(group, queue, ^{});
4.NSOperation與NSOperationQueue
為了對線程的操作更加的面向對象,APPLE對GCD進行了封裝,特將任務—->封裝成NSOperation
隊列——>封裝成NSOperationQueue
(1)NSOperation有兩個子類NSInvocationOperation以及NSBlockOperation
NSInvocationOperation的創建方法如下:
// 創建隊列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 創建操作
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download) object:nil];
// operation直接調用start,是同步執行(在當前線程執行操作)
// [operation start];
// 添加操作到隊列中,會自動異步執行
[queue addOperation:operation];
(2)NSBlockOperation的創建方法:
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@”—下載圖片—-11—%@”, [NSThread currentThread]);
}];
[operation1 addExecutionBlock:^{NSLog(@"---下載圖片----12---%@", [NSThread currentThread]);
}];NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"---下載圖片----2---%@", [NSThread currentThread]);
}];NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"---下載圖片----3---%@", [NSThread currentThread]);
}];NSBlockOperation *operation4 = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"---下載圖片----4---%@", [NSThread currentThread]);
}];// 1.創建隊列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];// 主隊列
// NSOperationQueue *queue = [NSOperationQueue mainQueue];
// 2.添加操作到隊列中(自動異步執行)
[queue addOperation:operation1];
[queue addOperation:operation2];
[queue addOperation:operation3];
[queue addOperation:operation4];
其中NSOperation類可以添加執行代碼塊:addExecutionBlock
【NSOperationQueue mainQueue】是獲取主隊列,其實底層相當于dispatch_get_mainQueue()
NSBlockOperation *operation = [[NSBlockOperation alloc] init];[operation addExecutionBlock:^{NSLog(@"---下載圖片----1---%@", [NSThread currentThread]);
}];[operation addExecutionBlock:^{NSLog(@"---下載圖片----2---%@", [NSThread currentThread]);
}];[operation addExecutionBlock:^{NSLog(@"---下載圖片----3---%@", [NSThread currentThread]);
}];[operation start];// 任務數量 > 1,才會開始異步執行
5.線程的通信
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperationWithBlock:^{
// 1.異步下載圖片
NSURL *url = [NSURL URLWithString:@”http://d.hiphotos.baidu.com/image/pic/item/37d3d539b6003af3290eaf5d362ac65c1038b652.jpg“];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
// 2.回到主線程,顯示圖片
// [self performSelectorOnMainThread:<#(SEL)#> withObject:<#(id)#> waitUntilDone:<#(BOOL)#>];
// dispatch_async(dispatch_get_main_queue(), ^{
//
// });
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.imageView.image = image;
}];
}];
控制Operation之間的運行順序可以通過添加依賴來實現
- (void)dependency
{
/**
假設有A、B、C三個操作,要求:
1. 3個操作都異步執行
2. 操作C依賴于操作B
3. 操作B依賴于操作A
*/
// 1.創建一個隊列(非主隊列)
NSOperationQueue *queue = [[NSOperationQueue alloc] init];// 2.創建3個操作
NSBlockOperation *operationA = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"A1---%@", [NSThread currentThread]);
}];
// [operationA addExecutionBlock:^{
// NSLog(@"A2---%@", [NSThread currentThread]);
// }];
//
// [operationA setCompletionBlock:^{
// NSLog(@"AAAAA---%@", [NSThread currentThread]);
// }];NSBlockOperation *operationB = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"B---%@", [NSThread currentThread]);
}];
NSBlockOperation *operationC = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"C---%@", [NSThread currentThread]);
}];// 設置依賴
[operationB addDependency:operationA];
[operationC addDependency:operationB];// 3.添加操作到隊列中(自動異步執行任務)
[queue addOperation:operationC];
[queue addOperation:operationA];
[queue addOperation:operationB];
}
為了達到優化app性能的需求,有時候是需要控制并發線程的數目以及隊列中的線程的運行狀態
- (void)maxCount
{
// 1.創建一個隊列(非主隊列)
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 2.設置最大并發(最多同時并發執行3個任務)
queue.maxConcurrentOperationCount = 3;// 3.添加操作到隊列中(自動異步執行任務,并發)
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"下載圖片1---%@", [NSThread currentThread]);
}];
NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"下載圖片2---%@", [NSThread currentThread]);
}];
NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"下載圖片3---%@", [NSThread currentThread]);
}];
NSBlockOperation *operation4 = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"下載圖片4---%@", [NSThread currentThread]);
}];
NSInvocationOperation *operation5 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download) object:nil];[queue addOperation:operation1];
[queue addOperation:operation2];
[queue addOperation:operation3];
[queue addOperation:operation4];
[queue addOperation:operation5];
[queue addOperationWithBlock:^{NSLog(@"下載圖片5---%@", [NSThread currentThread]);
}];
[queue addOperationWithBlock:^{NSLog(@"下載圖片6---%@", [NSThread currentThread]);
}];
[queue addOperationWithBlock:^{NSLog(@"下載圖片7---%@", [NSThread currentThread]);
}];
[queue addOperationWithBlock:^{NSLog(@"下載圖片8---%@", [NSThread currentThread]);
}];
[queue addOperationWithBlock:^{NSLog(@"下載圖片9---%@", [NSThread currentThread]);
}];[queue cancelAllOperations];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// [queue cancelAllOperations]; // 取消隊列中的所有任務(不可恢復)
}
(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
// [queue setSuspended:YES]; // 暫停隊列中的所有任務
}(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
// [queue setSuspended:NO]; // 恢復隊列中的所有任務
}