進程是指在系統中正在運行的一個應用程序。每一個進程之間是獨立的,每一個進程均運行在其專用且受保護的內存空間內。ios
好比同時打開 QQ、Xcode,系統就會分別啓動兩個進程。經過 「活動監視器」 能夠查看 Mac 系統中所開啓的進程。程序員
一個程序的一次運行,在執行過程當中擁有獨立的內存單元,而多個線程共享一塊內存。web
線程是進程中執行任務的基本執行單元。一個進程要執行任務,必須得有線程,一個進程(程序)的全部任務都在線程中執行。每個進程至少有一條線程,即主線程。一個進程能夠開啓多條線程,每條線程能夠併發(同時)執行不一樣的任務。數據庫
好比使用酷狗播放音樂、使用迅雷下載電影,都須要在線程中執行。swift
在程序中每個方法的執行,都是從上向下串行執行的。除非使用 block,不然在一個方法中,全部代碼的執行都在同一個線程上。安全
進程與線程的聯繫:線程是進程的基本組成單位。服務器
一個線程中任務的執行是串行(順序執行)的。若是要在一個線程中執行多個任務,那麼只能一個一個的按順序執行這些任務,也就是說,在同一時間內,一個線程只能執行一個任務。網絡
好比在一個線程中下載三個文件(分別是文件 A,文件 B,文件 C)。多線程
同一時間,CPU 只能處理一條線程,只有一條線程在工做(執行)。多線程併發(同時)執行,實際上是 CPU 快速的在多條線程之間調度(切換)。若是 CPU 調度線程的時間足夠快,就形成了多條線程併發執行的假象。閉包
多線程開發的複雜度相對較高,在開發時能夠按照如下套路編寫代碼:
在多線程開發的時候,有幾點提示:
線程越多,程序設計就更加複雜,好比線程之間的通訊、多線程的數據共享就更加複雜。
一般開啓 5 到 6 條線程便可,不能開啓的太多。
一個 iOS 程序運行後,默認會開啓一條線程,稱爲主線程或 UI 線程。主線程主要用於顯示/刷新 UI 界面,處理 UI 事件(好比點擊事件、滾動事件、拖拽事件等)。
注意別將比較耗時的操做放到主線程中,耗時的操做會卡住主線程,嚴重影響 UI 的流暢度,給用戶一種 「卡」 的壞體驗。
新線程建立啓動後,線程對象被加入可調度線程池中,進入就緒狀態,等待 CPU 調度。在線程被調度運行時,若是線程調用了 sleep 方法或者等待同步鎖時,線程由運行狀態進入到阻塞狀態,線程被移出可調度線程池。sleep 到時或者獲得同步鎖時,線程從新進入可調度線程池,回到就緒狀態。線程任務執行完畢或者異常、強制退出時,線程由運行狀態進入到死亡狀態,線程結束,線程佔用內存空間被釋放。
要實現線程安全,就必需要用到鎖,用到鎖就會下降程序的性能。爲了獲得更佳的用戶體驗,UIKit 不是線程安全的,UI 線程約定全部更新 UI 的操做都必須主線程上執行,所以,主線程又被稱爲 UI 線程。
1)iOS 開發建議:
2)使用互斥鎖:
@synchronized (self) { }
在 iOS 開發中,使用鎖的機會極少,通常都是從服務器獲取到數據,直接顯示。
3)使用變量原子性:
原子屬性,解決不了賣票問題,由於賣票的讀寫都須要鎖定。
定義屬性時,系統默認提供 getter & setter 方法,而且生成一個 _成員變量,可是若是本身重寫了 getter & setter 方法,_成員變量,不會自動生成。
在 Xcode 4.5 以前,開發,程序員必須本身實現 @synthesize 指令。
4)自旋鎖 & 互斥鎖:
在子線程中執行比較耗時的操做(以下載圖片等),子線程執行完畢後通知主線程更新 UI 等的操做。
UIKit 中幾乎全部控件都不是線程安全的,所以須要在主線程上更新 UI。在子線程中可使用如下方法讓主線程執行 UI 操做:
// waitUntilDone: YES 等待主線程執行完成後子線程再繼續執行 [self performSelectorOnMainThread:@selector(updateUI:) withObject:image waitUntilDone:YES]; dispatch_async(dispatch_get_main_queue(), ^{ [self updateUI:image]; }); [[NSOperationQueue mainQueue] addOperationWithBlock:^{ [self updateUI:image]; }];
pthread 是 POSIX 的多線程開發框架,因爲是跨平臺的 C 語言框架,在蘋果的頭文件中並無詳細的註釋。Xcode 中所用跨平臺的文件一般都在 usr/include 目錄下。對於全部跨平臺的框架(如:pthread & socket),能夠訪問一個網站: baike.baidu.com
iOS 中 C 語言框架:
一般,在 C 語言框架中,「對象」 類型以 _t/Ref 結尾,並且定義時不須要使用 * 。
內存管理:
參數橋接:
void *:
C 語言中的 void * 和 OC 中的 id 是等價的。
例如:
C : void *(*)(void *) OC : id (函數名) (id) 即 返回值類型(函數名)(參數) block : 返回值 (^) (參數) block 匿名的函數指針
GCD 是 Grand Central Dispatch(譯爲 「中樞調度器」)的簡稱,它是基於 C 語言編寫的,是蘋果公司爲多核的並行運算提出的解決方案。GCD 在工做時會自動利用更多的處理器核心,以充分利用更強大的機器。若是使用 GCD,徹底由系統管理線程,咱們不須要編寫線程代碼,只需定義想要執行的任務,而後添加到適當的調度隊列(dispatch queue),GCD 會負責建立線程和調度你的任務。它首次發佈在 Mac OS X 10.6 ,iOS 4 上。在 iOS 全部實現多線程的方案中,GCD 應該是最有魅力的,GCD 是一個替代諸如 NSThread, NSOperationQueue,NSInvocationOperation 等技術的很高效和強大的技術。
1) 工做原理:
將長期運行的任務拆分紅多個工做單元,並將這些單元添加到 dispath queue 中,系統會爲咱們管理這些 dispath queue,根據可用的處理資源,安排他們在任何可用的處理器核心上執行任務。咱們不須要直接啓動和管理後臺線程。一個任務能夠是一個函數(function)或者是一個 block。GCD 的底層依然是用線程實現,不過這樣可讓程序員不用關注實現的細節。
將任務添加到隊列,而且指定執行任務的函數,執行任務。任務使用 block 封裝,任務的 block 沒有參數也沒有返回值。
2) 執行任務的函數:
異步 dispatch_async
同步 dispatch_sync
在當前線程執行 block 的任務。
同步任務的做用:同步任務,可讓其餘異步執行的任務,"依賴" 某一個同步任務。例如:在用戶登陸以後,再異步下載文件!
3) 任務調度隊列:
GCD 中的 FIFO 隊列稱爲 dispatch queue(調度隊列)。系統提供了許多預約義的 dispatch queue,包括能夠保證始終在主線程上執行工做的 dispatch queue。也能夠建立本身的 dispatch queue,並且能夠建立任意多個。GCD 的 dispatch queue 嚴格遵循 FIFO(先進先出) 原則,添加到 dispatch queue 的工做單元將始終按照加入 dispatch queue 的順序啓動。dispatch queue 按先進先出的順序,串行或併發地調度任務在線程上實行。
dispatch queue 分爲下面三種:
Serial:
Concurrent:
併發隊列,又稱爲 global dispatch queues,一次能夠 "調度" 多個任務,儘量多地啓動任務併發執行,任務執行完成的順序是隨機的。
系統給每個應用程序提供了三個 concurrent dispatch queues。這三個併發調度隊列是全局的,它們只有優先級的不一樣。由於是全局的,咱們不須要去建立。咱們只須要經過使用函數 dispath_get_global_queue 去獲得隊列。
Main dispatch queue:
4) 各類隊列的執行效果:
Type | 全局併發隊列 | 手動建立串行隊列 | 主隊列
-----------------|--------------------|--------------------|-------------------
同步 (sync) | 沒有開啓新線程 | 沒有開啓新線程 | 沒有開啓新線程
~ | 串行執行任務 | 串行執行任務 | 串行執行任務
異步 (async) | 有開啓新線程 | 有開啓新線程 | 沒有開啓新線程
~ | 併發執行任務 | 串行執行任務 | 串行執行任務
開不開線程由執行任務的函數決定:
開幾條線程由隊列決定:
併發隊列開多條線程。
主隊列不會開啓線程。
5) 隊列的選擇:
多線程的目的:將耗時的操做放在後臺執行!
串行隊列,只開一條線程,全部任務順序執行
併發隊列,會開啓多條線程,全部任務不按照順序執行
實際開發中
6) 全局隊列 & 併發隊列的區別:
全局隊列:
併發隊列:
7) GCD & NSThread 對比:
NSOperation 也是蘋果公司推出的 「併發」 技術。是基於 OC 語言的,iOS 2.0 推出。GCD 推出以後,蘋果對 NSOperation 底層從新編寫過,是對 GCD 的封裝。Cocoa operation 相關的類是 NSOperation,NSOperationQueue。NSOperation 是個抽象類,使用它必須用它的子類,能夠實現它或者使用它定義好的兩個子類:NSInvocationOperation 和 NSBlockOperation。
優勢:不須要關心線程管理,數據同步的事情,能夠把精力放在本身須要執行的操做上。
1)NSOperation 與 GCD 對比:
NSOperation:
核心概念:把 "操做(異步執行的任務)" 添加到隊列(全局的併發隊列)。即建立 NSOperation 子類的對象,把對象添加到 NSOperationQueue 隊列裏執行。
OC 的框架,更加面向對象,是對 GCD 的封裝
GCD:
核心概念:將 "任務(block)" 添加到隊列(串行/併發/全局/主隊列),而且指定任務執行的函數(同步/異步)
C 語言的框架,dispatch_xxx 函數
2)自定義 NSOperation 操做流程:
Objective-C
// 添加頭文件 #import <pthread.h> // 建立 pthread 子線程 /* int pthread_create(pthread_t * __restrict, const pthread_attr_t * __restrict, void *(*)(void *), void * __restrict); 返回值: 線程建立成功,返回 0。線程建立失敗,返回出錯編號。 成功的緣由只有一個,失敗的緣由能夠有不少,在不少 C 語言框架中,都會採用這個套路。 參數: pthread_t * :第一個參數爲指向線程標識符的指針。 pthread_attr_t * :第二個參數用來設置線程屬性。 void *(*)(void *) :第三個參數是線程運行函數的起始地址。 void * :第四個參數是運行函數的參數。 */ pthread_t threadId = NULL; NSString *str = @"pthread"; int result = pthread_create(&threadId, NULL, pthreadDemo, (__bridge void *)(str)); if (result) { NSLog(@"線程建立失敗 %d", result); } else { NSLog(@"線程建立成功"); } // 子線程執行方法 void * pthreadDemo(void * param) { NSString *str = (__bridge NSString *)(param); NSLog(@"%@ --- %@", [NSThread currentThread], str); return NULL; }
- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(nullable id)argument; + (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullable id)argument; - (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg; public convenience init(target: AnyObject, selector: Selector, object argument: AnyObject?) public class func detachNewThreadSelector(selector: Selector, toTarget target: AnyObject, withObject argument: AnyObject?) public func performSelectorInBackground(aSelector: Selector, withObject arg: AnyObject?) 參數的意義: selector :線程執行的方法,這個 selector 只能有一個參數,並且不能有返回值。 target :selector 消息發送的對象。 argument :傳輸給 target 的惟一參數,也能夠是 nil。 第一種方式是先建立線程對象,而後再運行線程操做,在運行線程操做前能夠設置線程的優先級等線程信息。 第二種和第三種方式會直接建立線程而且開始運行線程。 第三種方式是 "NSObject" 的一個分類方法,能夠由任何繼承自 "NSObject" 的類對象調用該方法隱式建立並啓動一個子線程。
Objective-C
// 1. 建立一個子線程 NSThread *myThread = [[NSThread alloc] initWithTarget:self selector:@selector(downloadImage:) object:imageUrlPath]; [myThread start]; // 2. 建立並啓動一個子線程 [NSThread detachNewThreadSelector:@selector(downloadImage:) toTarget:self withObject:imageUrlPath]; // 3. 隱式建立並啓動一個子線程 [self performSelectorInBackground:@selector(downloadImage:) withObject:imageUrlPath]; // 子線程執行方法 - (void)downloadImage:(NSString *) urlPath { }
Swift
// 1. 建立一個子線程 let myThread = NSThread(target: self, selector: #selector(ViewController.downloadImage(_:)), object: imageUrlPath) myThread.start() // 2. 建立並啓動一個子線程 NSThread.detachNewThreadSelector(#selector(ViewController.downloadImage(_:)), toTarget: self, withObject: imageUrlPath) // 3. 隱式建立並啓動一個子線程 self.performSelectorInBackground(#selector(ViewController.downloadImage(_:)), withObject: imageUrlPath) // 子線程執行方法 func downloadImage(urlPath: String) { }
Objective-C
// 啓動子線程 /* 將線程對象加入可調度線程池等待 CPU 調度,線程執行完畢後,因爲內存空間已經被釋放,不能再次啓動 */ [myThread start]; // 通知線程取消 /* 能夠在外部終止線程執行,在線程執行方法中須要增長 isCancelled == YES,若是成立直接返回 */ [myThread cancel]; // 獲取線程名字 NSString *threadName = myThread.name; // 設置線程名字 /* 在多個線程開發時,能夠用來判斷究竟是誰在執行任務 在大的商業項目中,一般須要在程序崩潰時,獲取程序準確執行所在的線程 */ myThread.name = @"downloadImage"; // 設置線程的優先級 /* 範圍 0.0 到 1.0,默認爲 0.5,優先級高表示 CPU 調度的頻率相對較高。開發時儘可能不要修改 */ myThread.threadPriority = 1; // 判斷線程是否正在執行 readonly BOOL isExecuting = myThread.isExecuting; // 判斷線程是否完成 readonly BOOL isFinished = myThread.isFinished; // 判斷線程是否被取消 readonly BOOL isCancelled = myThread.isCancelled; // 獲取當前線程 NSThread *currentThread = [NSThread currentThread]; // 判斷當前線程是否爲主線程 /* 能夠在全部的多線程技術中使用 */ BOOL isMainThread = [[NSThread currentThread] isMainThread]; BOOL isMainThread = [NSThread isMainThread]; // 判斷是否爲多線程操做 BOOL isMultiThreaded = [NSThread isMultiThreaded]; // 引用主線程 /* 返回主線程對象 */ NSThread *mainThread = [NSThread mainThread]; // 獲取棧區大小 /* 線程執行前,主線程和子線程默認棧區大小都爲 512K,線程完成後,棧區大小 0K,內存空間被釋放 在之前的 iOS 版本,主線程棧區 1M,子線程是 512K,並且不能修改 */ NSUInteger stackSize = [NSThread currentThread].stackSize; // 設置棧區大小 /* 即 256 * 1024 = 256K,只有在當前線程中設置纔有效 */ [NSThread currentThread].stackSize = 256 * 1024; // 退出當前線程的執行 /* 使線程進入死亡狀態,線程被終止後,後續代碼都不會執行,不能在主線程中調用此方法 在須要手動內存管理的代碼中,在終止線程以前,應該注意釋放以前分配的對象 */ [NSThread exit]; // 休眠到指定的時間 /* 使線程進入阻塞狀態,線程暫時被移出可調度線程池 */ [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:10]]; // 休眠指定的時長 /* 使線程進入阻塞狀態,線程暫時被移出可調度線程池 */ [NSThread sleepForTimeInterval:10];
Swift
// 啓動子線程 /* 將線程對象加入可調度線程池等待 CPU 調度,線程執行完畢後,因爲內存空間已經被釋放,不能再次啓動 */ myThread.start() // 通知線程取消 /* 能夠在外部終止線程執行,在線程執行方法中須要增長 isCancelled == YES,若是成立直接返回 */ myThread.cancel() // 獲取線程名字 let threadName:String? = myThread.name // 設置線程名字 /* 在多個線程開發時,能夠用來判斷究竟是誰在執行任務 在大的商業項目中,一般須要在程序崩潰時,獲取程序準確執行所在的線程 */ myThread.name = "downloadImage" // 設置線程的優先級 /* 範圍 0.0 到 1.0,默認爲 0.5,優先級高表示 CPU 調度的頻率相對較高。開發時儘可能不要修改 */ myThread.threadPriority = 1 // 判斷線程是否正在執行 readonly let isExecuting:Bool = myThread.executing // 判斷線程是否完成 readonly let isFinished:Bool = myThread.finished // 判斷線程是否被取消 readonly let isCancelled:Bool = myThread.cancelled // 獲取當前線程 let currentThread:NSThread = NSThread.currentThread() // 判斷當前線程是否爲主線程 /* 能夠在全部的多線程技術中使用 */ let isMainThread:Bool = NSThread.currentThread().isMainThread let isMainThread:Bool = NSThread.isMainThread() // 判斷是否爲多線程操做 let isMultiThreaded:Bool = NSThread.isMultiThreaded() // 引用主線程 /* 返回主線程對象 */ let mainThread:NSThread = NSThread.mainThread() // 獲取棧區大小 /* 線程執行前,主線程和子線程默認棧區大小都爲 512K,線程完成後,棧區大小 0K,內存空間被釋放 在之前的 iOS 版本,主線程棧區 1M,子線程是 512K,並且不能修改 */ let stackSize:Int = NSThread.currentThread().stackSize // 設置棧區大小 /* 即 256 * 1024 = 256K,只有在當前線程中設置纔有效 */ NSThread.currentThread().stackSize = 256 * 1024 // 退出當前線程的執行 /* 使線程進入死亡狀態,線程被終止後,後續代碼都不會執行,不能在主線程中調用此方法 在須要手動內存管理的代碼中,在終止線程以前,應該注意釋放以前分配的對象 */ NSThread.exit() // 休眠到指定的時間 /* 使線程進入阻塞狀態,線程暫時被移出可調度線程池 */ NSThread.sleepUntilDate(NSDate(timeIntervalSinceNow: 10)) // 休眠指定的時長 /* 使線程進入阻塞狀態,線程暫時被移出可調度線程池 */ NSThread.sleepForTimeInterval(10)
在子線程中向 self 發送消息,讓主線程執行某個方法。waitUntilDone: YES 等待主線程執行完成後子線程再繼續,NO 主線程在執行方法的時候,子線程也同時運行。
Objective-C
// 在主線程中執行操做 [self performSelectorOnMainThread:@selector(updateUI:) withObject:image waitUntilDone:YES]; // 在另外一個線程中執行操做 NSThread *mainThread = [NSThread mainThread]; [self performSelector:@selector(updateUI:) onThread:mainThread withObject:image waitUntilDone:YES];
Swift
// 在主線程中執行操做 self.performSelectorOnMainThread(#selector(ViewController.updateUI(_:)), withObject: image, waitUntilDone: true) // 在另外一個線程中執行操做 let mainThread = NSThread.mainThread() self.performSelector(#selector(ViewController.updateUI(_:)), onThread: mainThread, withObject: image, waitUntilDone: true)
NSWillBecomeMultiThreadedNotification // 將要變成多線程 NSDidBecomeSingleThreadedNotification // 已經變成單線程 NSThreadWillExitNotification // 將要退出子線程
Objective-C
// 添加線程系統通知 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(downloadEnd:) name:NSThreadWillExitNotification object:nil]; // 系統通知響應觸發事件 - (void)downloadEnd:(NSNotification *)notification { NSThread *thread = notification.object; NSLog(@"%@ 線程結束了", thread.name); }
Swift
// 添加線程系統通知 NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ViewController.downloadEnd(_:)), name: NSThreadWillExitNotification, object: nil) // 系統通知響應觸發事件 func downloadEnd(notification: NSNotification){ let thread = notification.object as! NSThread print("\(thread.name) 線程結束了"); }
Objective-C
// 對線程加鎖 // 操做變量時須要加線程鎖,保證同時只有一個線程在訪問該變量。 // 實例化線程鎖 NSLock *threadLock; _threadLock = [[NSLock alloc] init]; // 打開線程鎖,開始對變量寫操做 [_threadLock lock]; // 關閉線程鎖,中止對變量寫操做 [_threadLock unlock]; // 使用互斥鎖 @synchronized (self) { // 對變量寫操做 } // 聲明變量爲原子性 // 聲明變量爲原子性(默認) @property(assign) NSInteger page;
Swift
// 對線程加鎖 // 操做變量時須要加線程鎖,保證同時只有一個線程在訪問該變量。 // 實例化線程鎖 var threadLock:NSLock! threadLock = NSLock() // 打開線程鎖,開始對變量寫操做 threadLock.lock() // 關閉線程鎖,中止對變量寫操做 threadLock.unlock()
定義屬性時,系統默認提供 getter & setter 方法,而且生成一個 _成員變量,可是若是本身重寫了 getter & setter 方法,_成員變量,不會自動生成。
在 Xcode 4.5 以前,開發,程序員必須本身實現 @synthesize 指令。
Objective-C
@property (atomic, strong) NSObject *obj1; @property (atomic, strong) NSObject *obj2;
原子屬性模擬
@synthesize obj1 = _obj1; // obj1 - getter - (NSObject *)obj1 { return _obj1; } // obj1 - setter - (void)setObj1:(NSObject *)obj1 { // 使用互斥鎖 @synchronized(self) { _obj1 = obj1; } }
自旋鎖&互斥鎖性能測試
long largeNumber = 1000 * 1000; // 互斥鎖測試 // 2001-01-01 00:00:00 到如今的秒數 CFAbsoluteTime start = CFAbsoluteTimeGetCurrent(); for (int i = 0; i < largeNumber; ++i) { self.obj1 = [[NSObject alloc] init]; } NSLog(@"互斥鎖: %f", CFAbsoluteTimeGetCurrent() - start); // 自旋鎖測試 start = CFAbsoluteTimeGetCurrent(); for (int i = 0; i < largeNumber; ++i) { self.obj2 = [[NSObject alloc] init]; } NSLog(@"自旋鎖: %f", CFAbsoluteTimeGetCurrent() - start);
一、dispatch_async
經常使用的方法 dispatch_async,爲了不界面在處理耗時的操做時卡死,好比讀取網絡數據,IO,數據庫讀寫等,咱們會在另一個線程中處理這些操做,而後通知主線程更新界面。
dispatch_async(dispatch_get_global_queue(0, 0), ^{ // 耗時的操做 Code dispatch_async(dispatch_get_main_queue(), ^{ // 更新界面 Code }); });
二、dispatch_barrier_async
三、dispatch_group_async
dispatch_group_async 能夠實現監聽一組任務是否完成,等到 group 中的全部任務執行完畢後,由隊列調度 dispatch_group_notify block 中的任務異步執行。須要在全部異步任務執行完畢後,統一得到一個通知。group 負責監控任務,queue 負責調度任務。
dispatch_group_async 是異步的方法,任務的執行順序不肯定,與任務添加的順序無關。全部 dispatch_group_async 添加的任務都執行完後,再執行 dispatch_group_notify 添加的任務,但 dispatch_group_notify 添加的任務需最後添加。這個方法頗有用,好比你執行兩個下載任務,當兩個任務都下載完成後你才通知界面說完成的了。
四、dispatch_apply
Objective-C
全局隊列同步執行任務
// 全局隊列,負責調度任務 dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0); // 任務,使用 block 來包裝任務,block 沒有參數,沒有返回值 void (^task)() = ^ { }; // 指定執行任務的函數,不會開啓線程,就在當前線程執行 block dispatch_sync(globalQueue, task);
全局隊列異步執行任務
// 全局隊列,負責調度任務 dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0); // 任務,使用 block 來包裝任務,block 沒有參數,沒有返回值 void (^task)() = ^ { }; // 指定執行任務的函數,會開啓線程,在其餘線程執行 block dispatch_async(globalQueue, task);
建立 dispatch_async 線程
dispatch_async(dispatch_get_global_queue(0, 0), ^{ // 耗時的操做 Code NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:imageUrlPath]]; UIImage *image = [[UIImage alloc] initWithData:data]; dispatch_async(dispatch_get_main_queue(), ^{ // 更新界面,刷新主線程 Code self.imageView.image = image; }); });
順序操做
// myQueue 不能使用全局的對列 dispatch_queue_t myQueue = dispatch_queue_create("qq", DISPATCH_QUEUE_CONCURRENT); dispatch_async(myQueue, ^{ NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:imageUrlPath]]; _backImage = [[UIImage alloc] initWithData:data]; }); dispatch_async(myQueue, ^{ NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:iconImageUrlPath]]; _iconImage = [[UIImage alloc] initWithData:data]; }); dispatch_barrier_async(myQueue, ^{ dispatch_async(dispatch_get_main_queue(), ^{ self.iconImageView.image = _iconImage; }); }); dispatch_async(myQueue, ^{ dispatch_async(dispatch_get_main_queue(), ^{ self.imageView.image = _backImage; }); });
羣組操做,線程通知
// queue 負責調度任務 dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0); // group 負責監控任務 dispatch_group_t group = dispatch_group_create(); // 第一個任務 dispatch_group_async(group, globalQueue, ^{ NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:imageUrlPath]]; _backImage = [[UIImage alloc] initWithData:data]; }); // 第二個任務 dispatch_group_async(group, globalQueue, ^{ NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:iconImageUrlPath]]; _iconImage = [[UIImage alloc] initWithData:data]; }); // 其它全部添加的任務都執行完後,再執行該任務,但該任務需最後添加 dispatch_group_notify(group, dispatch_get_main_queue(), ^{ self.imageView.image = _backImage; self.iconImageView.image = _iconImage; });
羣組操做實現原理
/* The dispatch_group_async() convenience function behaves like so: void dispatch_group_async(dispatch_group_t group, dispatch_queue_t queue, dispatch_block_t block) { dispatch_retain(group); dispatch_group_enter(group); dispatch_async(queue, ^{ block(); dispatch_group_leave(group); dispatch_release(group); }); } */ // queue 負責調度任務 dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0); // group 負責監控任務 dispatch_group_t group = dispatch_group_create(); // 入組,以後的 block 會被 group 監聽 dispatch_group_enter(group); dispatch_async(globalQueue, ^{ NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:imageUrlPath]]; _backImage = [[UIImage alloc] initWithData:data]; // 出組,block 的末尾,全部任務執行完畢後,添加一個出組,dispatch_group_enter & dispatch_group_leave 必須成對出現 dispatch_group_leave(group); }); // 入組,以後的 block 會被 group 監聽 dispatch_group_enter(group); dispatch_async(globalQueue, ^{ NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:iconImageUrlPath]]; _iconImage = [[UIImage alloc] initWithData:data]; // 出組,block 的末尾,全部任務執行完畢後,添加一個出組,dispatch_group_enter & dispatch_group_leave 必須成對出現 dispatch_group_leave(group); }); // 阻塞式等待調度組中全部任務執行完畢 dispatch_group_wait(group, DISPATCH_TIME_FOREVER); // 羣組結束 dispatch_group_notify(group, dispatch_get_main_queue(), ^{ self.imageView.image = _backImage; self.iconImageView.image = _iconImage; });
延遲操做
// 從如今開始 n 納秒後,1.0 * NSEC_PER_SEC = 1 秒 dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)); // 延遲指定時間(納秒)後在指定的隊列中調度執行,異步執行 dispatch_after(when, dispatch_get_global_queue(0, 0), ^{ NSLog(@"%@", [NSThread currentThread]); });
循環操做
// 循環執行設定的次數(5 次),同步執行 dispatch_apply(5, dispatch_get_global_queue(0, 0), ^(size_t index) { NSLog(@"dispatch_apply: %zu --- %@", index, [NSThread currentThread]); });
一次性操做
/* 有的時候,在程序開發中,有些代碼只想從程序啓動就只執行一次,典型的應用場景就是 「單例」。 dispatch_once 可以保證 block 中的代碼,只會被執行一次,onceToken == 0 時就會執行 block 的代碼,執行後 變爲 -1, dispatch 內部也有一把鎖,是可以保證 "線程安全" 的,並且是蘋果公司推薦使用的。 block 同步執行,可以保證後續的代碼直接使用 block 執行後的結果。 */ // 同步執行 static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ });
同步任務的做用
// 同步任務,可讓其餘異步執行的任務,"依賴" 某一個同步任務。例如:在用戶登陸以後,再異步下載文件。 // 隊列 dispatch_queue_t q = dispatch_queue_create("qq", DISPATCH_QUEUE_CONCURRENT); // 異步執行 task dispatch_async(q, ^{ dispatch_sync(q, ^{ NSLog(@"Login %@", [NSThread currentThread]); }); dispatch_async(q, ^{ NSLog(@"Download A %@", [NSThread currentThread]); }); dispatch_async(q, ^{ NSLog(@"Download B %@", [NSThread currentThread]); }); });
主隊列同步任務不死鎖
// 主線程中主隊列同步執行時,主隊列和主線程相互等待會形成死鎖。在子線程中執行不會死鎖。 // 併發隊列 dispatch_queue_t q = dispatch_queue_create("qq", DISPATCH_QUEUE_CONCURRENT); // 任務 void (^task)() = ^ { dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"come here %@", [NSThread currentThread]); }); }; // 異步執行任務 dispatch_async(q, task);
Swift
全局隊列同步執行任務
// 全局隊列,負責調度任務 let q:dispatch_queue_t = dispatch_get_global_queue(0, 0); // 任務,使用 閉包 來包裝任務,閉包 沒有參數,沒有返回值 let task:(() -> Void) = { } // 指定執行任務的函數,不會開啓線程,就在當前線程執行 閉包 dispatch_sync(q, task);
全局隊列異步執行任務
// 全局隊列,負責調度任務 let q:dispatch_queue_t = dispatch_get_global_queue(0, 0); // 任務,使用 閉包 來包裝任務,閉包 沒有參數,沒有返回值 let task:(() -> Void) = { } // 指定執行任務的函數,會開啓線程,在其餘線程執行 閉包 dispatch_async(q, task);
建立 dispatch_async 線程
dispatch_async(dispatch_get_global_queue(0, 0)) { // 耗時的操做 Code let data = NSData(contentsOfURL: NSURL(string: self.imageUrlPath)!) let image = UIImage(data: data!) dispatch_async(dispatch_get_main_queue()) { // 更新界面,刷新主線程 Code self.imageView.image = image } }
順序操做
// myQueue 不能使用全局的序列 let myQueue:dispatch_queue_t = dispatch_queue_create("qq", DISPATCH_QUEUE_CONCURRENT) dispatch_async(myQueue) { let data = NSData(contentsOfURL: NSURL(string: self.imageUrlPath)!) self.backImage = UIImage(data: data!) } dispatch_async(myQueue) { let data = NSData(contentsOfURL: NSURL(string: self.iconImageUrlPath)!) self.iconImage = UIImage(data: data!) } dispatch_barrier_async(myQueue) { dispatch_async(dispatch_get_main_queue(), { self.iconImageView.image = self.iconImage }) NSThread.sleepForTimeInterval(4) } dispatch_async(myQueue) { dispatch_async(dispatch_get_main_queue(), { self.imageView.image = self.backImage }) }
羣組操做
// queue 負責調度任務 let globalQueue = dispatch_get_global_queue(0, 0) // group 負責監控任務 let group = dispatch_group_create() // 第一個任務 dispatch_group_async(group, globalQueue) { let data = NSData(contentsOfURL: NSURL(string: self.imageUrlPath)!) self.backImage = UIImage(data: data!) print("第一個任務完成") } // 第二個任務 dispatch_group_async(group, globalQueue) { let data = NSData(contentsOfURL: NSURL(string: self.iconImageUrlPath)!) self.iconImage = UIImage(data: data!) print("第二個任務完成") } // 其它全部添加的任務都執行完後,再執行該任務,但該任務需最後添加 dispatch_group_notify(group, dispatch_get_main_queue()) { // 異步執行 self.imageView.image = self.backImage self.iconImageView.image = self.iconImage print("更新界面") }
羣組操做實現原理
/* The dispatch_group_async() convenience function behaves like so: void dispatch_group_async(dispatch_group_t group, dispatch_queue_t queue, dispatch_block_t block) { dispatch_retain(group); dispatch_group_enter(group); dispatch_async(queue, ^{ block(); dispatch_group_leave(group); dispatch_release(group); }); } */ // queue 負責調度任務 let globalQueue = dispatch_get_global_queue(0, 0) // group 負責監控任務 let group = dispatch_group_create() // 入組,以後的 block 會被 group 監聽 dispatch_group_enter(group) dispatch_async(globalQueue) { let data = NSData(contentsOfURL: NSURL(string: self.imageUrlPath)!) self.backImage = UIImage(data: data!) // 出組,block 的末尾,全部任務執行完畢後,添加一個出,dispatch_group_enter & dispatch_group_leave 必須成對出現 dispatch_group_leave(group) } // 入組,以後的 block 會被 group 監聽 dispatch_group_enter(group) dispatch_async(globalQueue) { let data = NSData(contentsOfURL: NSURL(string: self.iconImageUrlPath)!) self.iconImage = UIImage(data: data!) // 出組,block 的末尾,全部任務執行完畢後,添加一個出組,dispatch_group_enter & dispatch_group_leave 必須成對出現 dispatch_group_leave(group) } // 阻塞式等待調度組中全部任務執行完畢 dispatch_group_wait(group, DISPATCH_TIME_FOREVER) // 羣組結束 dispatch_group_notify(group, dispatch_get_main_queue()) { // 異步執行 self.imageView.image = self.backImage self.iconImageView.image = self.iconImage }
延遲操做
// 從如今開始 n 納秒後,1 * NSEC_PER_SEC = 1 秒 let when:dispatch_time_t = dispatch_time(DISPATCH_TIME_NOW, (Int64)(1 * NSEC_PER_SEC)) // 延遲指定時間(納秒)後在指定的隊列中調度執行,異步執行 dispatch_after(when, dispatch_get_global_queue(0, 0)) { }
循環操做
// 循環執行設定的次數(5 次),同步執行 dispatch_apply(5, dispatch_get_global_queue(0, 0)) { (index:Int) in }
一次性操做
/* 有的時候,在程序開發中,有些代碼只想從程序啓動就只執行一次,典型的應用場景就是 「單例」。 dispatch_once 可以保證 block 中的代碼,只會被執行一次,onceToken == 0 時就會執行 block 的代碼,執行後 變爲 -1, dispatch 內部也有一把鎖,是可以保證 "線程安全" 的,並且是蘋果公司推薦使用的。 block 同步執行,可以保證後續的代碼直接使用 block 執行後的結果。 */ // 同步執行 struct Static { static var onceToken: dispatch_once_t = 0 } dispatch_once(&Static.onceToken, { })
同步任務的做用
// 同步任務,可讓其餘異步執行的任務,"依賴" 某一個同步任務。例如:在用戶登陸以後,再異步下載文件。 // 隊列 let q:dispatch_queue_t = dispatch_queue_create("qq", DISPATCH_QUEUE_CONCURRENT) dispatch_async(q) { // 異步執行 task dispatch_sync(q, { }) dispatch_async(q, { }) dispatch_async(q, { }) }
主隊列同步任務不死鎖
// 主線程中主隊列同步執行時,主隊列和主線程相互等待會形成死鎖。在子線程中執行不會死鎖。 // 隊列 let q:dispatch_queue_t = dispatch_queue_create("qq", DISPATCH_QUEUE_CONCURRENT) // 任務 let task:(() -> Void) = { dispatch_async(dispatch_get_main_queue(), { }) } // 異步執行任務 dispatch_async(q, task)
Objective-C
調度組的建立
// 建立調度組 dispatch_group_t group = dispatch_group_create();
線程隊列的建立
// 獲取全局隊列 dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0); // 獲取主線程隊列 dispatch_queue_t mainQueue = dispatch_get_main_queue(); // 建立串行隊列 dispatch_queue_t mySerialQueue = dispatch_queue_create("mySerialQueue", NULL); // 建立併發隊列 dispatch_queue_t myConcurrentQueue = dispatch_queue_create("myConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);
全局隊列的獲取
/* dispatch_queue_t dispatch_get_global_queue(long identifier, unsigned long flags); 全局隊列,是 GCD 爲了方便程序員的多線程開發提供的 dispatch_get_global_queue,自己就是一個併發隊列。 參數: 1. identifier:服務質量(隊列對任務調度的優先級)/iOS 7.0 以前,是優先級 iOS 8.0+ 告訴隊列執行任務的 "服務質量 quality of service": QOS_CLASS_USER_INTERACTIVE 0x21, 用戶交互(但願儘快完成,用戶對結果很指望,不要放太耗時操做) QOS_CLASS_USER_INITIATED 0x19, 用戶指望(但願快,不要放太耗時操做) QOS_CLASS_DEFAULT 0x15, 默認(不是給程序員使用的,用來重置對列使用的) QOS_CLASS_UTILITY 0x11, 實用工具(耗時操做,專門用來處理耗時操做) QOS_CLASS_BACKGROUND 0x09, 後臺 QOS_CLASS_UNSPECIFIED 0x00, 未指定,能夠和 iOS 7.0 適配 iOS 7.0 及以前 優先級: DISPATCH_QUEUE_PRIORITY_HIGH 2 高優先級 DISPATCH_QUEUE_PRIORITY_DEFAULT 0 默認優先級 DISPATCH_QUEUE_PRIORITY_LOW (-2) 低優先級 DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN 後臺優先級 提示:不要選擇 BACKGROUND 的選項,蘋果認爲:BACKGROUND 表示用戶不須要知道任務何時完成。選擇這個選項,速度慢的使人髮指!不利於調試。 關於優先級,不要搞太負責,就用最簡單的。 結論:若是要作 iOS 8.0 & iOS 7.0 的適配,使用如下代碼:dispatch_get_global_queue(0, 0); 若是要作 iOS 8.0 & iOS 9.0 的適配,應該選擇 QOS_CLASS_UTILITY 2. flags:保留 標記是爲了將來使用保留的。這個參數應該永遠指定爲 0。 */ // 獲取全局隊列 dispatch_queue_t globalQueue = dispatch_get_global_queue(QOS_CLASS_UTILITY, 0);
Swift
調度組的建立
// 建立調度組 let group:dispatch_group_t = dispatch_group_create()
線程隊列的建立
// 獲取全局隊列 let globalQueue:dispatch_queue_t = dispatch_get_global_queue(0, 0) // 獲取主線程隊列 let mainQueue:dispatch_queue_t = dispatch_get_main_queue() // 建立串行隊列 let mySerialQueue:dispatch_queue_t = dispatch_queue_create("mySerialQueue", nil); // 建立併發隊列 let myConcurrentQueue:dispatch_queue_t = dispatch_queue_create("myConcurrentQueue", DISPATCH_QUEUE_CONCURRENT)
在子線程中調用主線程隊列,執行刷新 UI 等操做。
Objective-C
dispatch_async(dispatch_get_main_queue(), ^{ // 更新界面,刷新主線程 Code });
Swift
dispatch_async(dispatch_get_main_queue()) { // 更新界面,刷新主線程 Code }
NSOpeartion 是對 GCD 的封裝,是 OC 的,比 GCD 的使用簡單。即將 「操做」 添加到隊列。
使用 NSOperation 的方式有兩種:
Objective-C
建立一個 block 風格的任務
// 建立任務操做隊列 NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init]; NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ [self downloadImage1:imageUrlPath]; }]; // 將一個操做任務加到隊列中,若是隊列中的任務數小於最大併發數,會當即執行,不然任務排隊 [operationQueue addOperation:operation];
直接向隊列添加 block
// 建立任務操做隊列 NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init]; // 直接向操做隊列添加操做 block [operationQueue addOperationWithBlock:^{ [self downloadImage1:imageUrlPath]; }];
含主隊列的任務
[[[NSOperationQueue alloc] init] addOperationWithBlock:^{ // 耗時的操做 Code NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:imageUrlPath]]; UIImage *image = [[UIImage alloc] initWithData:data]; [[NSOperationQueue mainQueue] addOperationWithBlock:^{ // 更新界面,刷新主線程 Code self.imageView.image = image; }]; }];
建立一個普通的任務
// 建立任務操做隊列 NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init]; // 建立一個普通的操做任務 NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(downloadImage1:) object:imageUrlPath]; // 將一個操做任務加到隊列中,若是隊列中的任務數小於最大併發數,會當即執行,不然任務排隊 [operationQueue addOperation:operation];
子線程執行方法
- (void)downloadImage:(NSString *) urlPath { }
Swift
建立一個 block 風格的任務
// 建立任務操做隊列 let operationQueue = NSOperationQueue() let operation = NSBlockOperation { self.downloadImage1(self.imageUrlPath) } // 將一個操做任務加到隊列中,若是隊列中的任務數小於最大併發數,會當即執行,不然任務排隊 operationQueue.addOperation(operation)
直接向隊列添加 block
// 建立任務操做隊列 let operationQueue = NSOperationQueue() // 直接向操做隊列添加操做 block operationQueue.addOperationWithBlock { self.downloadImage1(self.imageUrlPath) }
含主隊列的任務
NSOperationQueue().addOperationWithBlock { // 耗時的操做 Code let data = NSData(contentsOfURL: NSURL(string: self.imageUrlPath)!) let image = UIImage(data: data!) NSOperationQueue.mainQueue().addOperationWithBlock({ // 更新界面,刷新主線程 Code self.imageView.image = image }) }
子線程執行方法
func downloadImage(urlPath: String) { }
Objective-C
隊列設置
// 建立全局併發隊列 NSOperationQueue *globalQueue = [[NSOperationQueue alloc] init]; // 獲取當前隊列 NSOperationQueue *currentQueue = [NSOperationQueue currentQueue]; // 獲取主隊列 NSOperationQueue *mainQueue = [NSOperationQueue mainQueue]; // 向隊列添加操做 [operationQueue addOperation:operation]; // 向隊列添加多個操做 /* NO 異步的,YES 同步的 */ [operationQueue addOperations:@[operation1, operation2] waitUntilFinished:NO]; // 向隊列添加操做 block [operationQueue addOperationWithBlock:^{ }]; // 設置最大併發數 /* 默認狀況下是 -1,-1 表示沒有限制,會同時運行隊列中的所有的操做 */ operationQueue.maxConcurrentOperationCount = 4; // 設置隊列名稱 operationQueue.name = @"myOperationQueue"; // 設置隊列服務質量 operationQueue.qualityOfService = NSQualityOfServiceUtility; // 設置隊列是否掛起 /* YES 掛起,NO 繼續執行 隊列掛起,當前 "沒有完成的操做" 是包含在隊列的操做數中的隊列掛起,不會影響已經執行操做的執行狀態 對列一旦被掛起,再添加的操做不會被調度 */ operationQueue.suspended = YES; // 判斷隊列是否掛起 BOOL isSuspended = operationQueue.isSuspended; // 給隊列中全部操做發送取消(cancel)消息 /* 在隊列取消完成以前,操做計數並不會發生變化 系統的全部方法都沒有對 isCancelled 作判斷 正在執行的操做不會響應 cancel 消息,不會取消正在執行中的操做,不會影響隊列的掛起狀態 */ [operationQueue cancelAllOperations]; // 獲取隊列中操做的數量 NSUInteger operationCount = operationQueue.operationCount; // 獲取隊列中的全部操做 NSArray *operations = operationQueue.operations; // 等待全部操做完成 [operationQueue waitUntilAllOperationsAreFinished];
操做設置
// 設置操做間依賴關係 /* operation2 依賴於 operation1 */ [operation2 addDependency:operation1]; // 移除操做間依賴關係 [operation2 removeDependency:operation1]; // 獲取依賴的全部操做,readonly NSArray *dependencies = operation2.dependencies; // 取消操做 [operation cancel]; // 判斷操做是否被取消,readonly BOOL isCancelled = operation.isCancelled; // 判斷操做是否正在被執行,readonly BOOL isExecuting = operation.isExecuting; // 判斷操做是否執行完成,readonly BOOL isFinished = operation.isFinished; // 判斷操做是不是異步執行的,readonly BOOL isAsynchronous = operation.isAsynchronous; // 判斷操做是否準備好執行,readonly BOOL isReady = operation.isReady; // 設置操做名稱 operation.name = @"myOperation"; // 設置操做隊列優先級 operation.queuePriority = NSOperationQueuePriorityNormal; // 設置操做服務質量 operation.qualityOfService = NSQualityOfServiceUtility; // 等待操做完成 [operation waitUntilFinished]; // 在當前線程中開始執行操做 [operation start]; // 設置操做完成的回調 [operation setCompletionBlock:^{ NSLog(@"任務一完成了"); }]; // 設置操做完成的回調 [operationQueue.operations[0] setCompletionBlock:^{ NSLog(@"任務一完成了"); }];
Swift
隊列設置
// 建立全局併發隊列 let globalQueue:NSOperationQueue = NSOperationQueue() // 獲取當前隊列 let currentQueue:NSOperationQueue? = NSOperationQueue.currentQueue() // 獲取住隊列 let mainQueue:NSOperationQueue = NSOperationQueue.mainQueue() // 向隊列添加操做 operationQueue.addOperation(operation) // 向隊列添加多個操做 /* NO 異步的,YES 同步的 */ operationQueue.addOperations([operation1, operation2], waitUntilFinished: false) // 向隊列添加操做 block operationQueue.addOperationWithBlock { } // 設置最大併發數 /* 默認狀況下是 -1,-1 表示沒有限制,會同時運行隊列中的所有的操做 */ operationQueue.maxConcurrentOperationCount = 4 // 設置隊列名稱 operationQueue.name = "myOperationQueue" // 設置隊列服務質量 operationQueue.qualityOfService = .Utility // 設置隊列是否掛起 /* YES 掛起,NO 繼續執行 隊列掛起,當前 "沒有完成的操做" 是包含在隊列的操做數中的隊列掛起,不會影響已經執行操做的執行狀態 對列一旦被掛起,再添加的操做不會被調度 */ operationQueue.suspended = true // 判斷隊列是否掛起 let isSuspended:Bool = operationQueue.suspended // 給隊列中全部操做發送取消(cancel)消息 /* 在隊列取消完成以前,操做計數並不會發生變化 系統的全部方法都沒有對 isCancelled 作判斷 正在執行的操做不迴響應 cancel 消息,不會取消正在執行中的操做,不會影響隊列的掛起狀態 */ operationQueue.cancelAllOperations() // 獲取隊列中操做的數量 let operationCount:Int = operationQueue.operationCount // 獲取隊列中的全部操做 let operations:[NSOperation] = operationQueue.operations // 等待全部操做完成 operationQueue.waitUntilAllOperationsAreFinished()
操做設置
// 設置操做間依賴關係 /* operation2 依賴於 operation1 */ operation2.addDependency(operation1) // 移除操做間依賴關係 operation2.removeDependency(operation1) // 獲取依賴的全部操做,readonly let dependencies:[NSOperation] = operation2.dependencies // 取消操做 operation.cancel() // 判斷操做是否被取消,readonly let isCancelled:Bool = operation.cancelled // 判斷操做是否正在被執行,readonly let isExecuting:Bool = operation.executing // 判斷操做是否執行完成,readonly let isFinished:Bool = operation.finished // 判斷操做是不是異步執行的,readonly let isAsynchronous:Bool = operation.asynchronous // 判斷操做是否準備好執行,readonly let isReady:Bool = operation.ready // 設置操做名稱 operation.name = "myOperation" // 設置操做隊列優先級 operation.queuePriority = .Normal // 設置操做服務質量 operation.qualityOfService = .Utility // 等待操做完成 operation.waitUntilFinished() // 在當前線程中開始執行操做 operation.start() // 設置操做完成的回調 operation.completionBlock = { print("任務一完成了") } // 設置操做完成的回調 operationQueue.operations[0].completionBlock = { print("任務一完成了") }
子線程裏不容許操做 UI
Objective-C
[[[NSOperationQueue alloc] init] addOperationWithBlock:^{ // 在子隊列中執行耗時的操做 [[NSOperationQueue mainQueue] addOperationWithBlock:^{ // 在主隊列中執行刷新 UI 等操做 }]; }];
Swift
NSOperationQueue().addOperationWithBlock { // 在子隊列中執行耗時的操做 NSOperationQueue.mainQueue().addOperationWithBlock({ // 在主隊列中執行刷新 UI 等操做 }) }
Objective-C
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"用戶登陸 %@", [NSThread currentThread]); }]; NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"付費 %@", [NSThread currentThread]); }]; NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{ [NSThread sleepForTimeInterval:1.0f]; NSLog(@"下載 %@", [NSThread currentThread]); }]; NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"更新UI %@", [NSThread currentThread]); }]; // 付費以前須要登陸,在指定依賴關係時,不要出現循環依賴 [op2 addDependency:op1]; // 下載以前須要付費 [op3 addDependency:op2]; // 更新UI以前須要完成下載 [op4 addDependency:op3]; // NO 異步的,YES 同步的 [[[NSOperationQueue alloc] init] addOperations:@[op1, op2, op3] waitUntilFinished:NO]; // 更新 UI 的操做,應該由主隊列來調度 [[NSOperationQueue mainQueue] addOperation:op4];
Swift
let op1 = NSBlockOperation.init { print("用戶登陸 \(NSThread.currentThread())") } let op2 = NSBlockOperation.init { print("付費 \(NSThread.currentThread())") } let op3 = NSBlockOperation.init { print("下載 \(NSThread.currentThread())") } let op4 = NSBlockOperation.init { print("更新UI \(NSThread.currentThread())") } // 付費以前須要登陸,在指定依賴關係時,不要出現循環依賴 op2.addDependency(op1) // 下載以前須要付費 op3.addDependency(op2) // 更新UI以前須要完成下載 op4.addDependency(op3) // NO 異步的,YES 同步的 NSOperationQueue().addOperations([op1, op2, op3], waitUntilFinished: false) // 更新 UI 的操做,應該由主隊列來調度 NSOperationQueue.mainQueue().addOperation(op4)
Objective-C
WebImageOperation.h
@interface WebImageOperation : NSOperation /// 實例化 web 圖像操做 + (instancetype)webImageOperationWithURLString:(NSString *)urlString completion:(void (^)(UIImage *image))completion; @end
WebImageOperation.m
/// 下載圖片的 URL @property (nonatomic, copy) NSString *urlStr; /// 下載完成的回調 @property (nonatomic, copy) void (^completion) (UIImage *image); + (instancetype)webImageOperationWithURLString:(NSString *)urlString completion:(void (^)(UIImage *))completion { WebImageOperation *imageOperation = [[self alloc] init]; imageOperation.urlStr= urlString; imageOperation.completion = completion; return imageOperation; } // 操做加入隊列後會自動執行該方法 - (void)main { @autoreleasepool { if (self.isCancelled) return; NSURL *url = [NSURL URLWithString:self.urlStr]; NSData *data = [NSData dataWithContentsOfURL:url]; if (self.isCancelled) return; if (self.completion && data != nil) { [[NSOperationQueue mainQueue] addOperationWithBlock:^{ self.completion([UIImage imageWithData:data]); }]; } } }
ViewController.m
// 自定義 NSOperation 操做 WebImageOperation *downloadOperation = [WebImageOperation webImageOperationWithURLString:imageUrlPath completion:^(UIImage *image) { self.imageView.image = image; }]; // 將操做添加到隊列 [[[NSOperationQueue alloc] init] addOperation:downloadOperation];
Swift
WebImageOperation.swift
class WebImageOperation: NSOperation /// 下載圖片的 URL var urlStr:String! /// 下載完成的回調 var completion:((image:UIImage) -> Void)! class func webImageOperationWithURLString(urlString:String, completion:((image:UIImage) -> Void)) -> WebImageOperation { let imageOperation:WebImageOperation = WebImageOperation() imageOperation.urlStr = urlString imageOperation.completion = completion return imageOperation } // 操做加入隊列後會自動執行該方法 override func main() { if self.cancelled == true { return } let url:NSURL = NSURL(string: self.urlStr)! let data:NSData? = NSData(contentsOfURL: url) if self.cancelled == true { return } if (self.completion != nil) && (data != nil) { NSOperationQueue.mainQueue().addOperationWithBlock({ self.completion(image: UIImage(data: data!)!) }) } }
ViewController.swift
// 自定義 NSOperation 操做 let downloadOperation:WebImageOperation = WebImageOperation.webImageOperationWithURLString(imageUrlPath) { (image:UIImage) in self.imageView.image = image } // 將操做添加到隊列 NSOperationQueue().addOperation(downloadOperation)