一、NSOperation介紹:基本語法
NSOperation在iOS4后也基于GCD實現,但是相對于GCD來說可控性更強,并且可以加入操作依賴。NSOperation是一個抽象類,系統為我們提供了NSBlockOperation和NSInvocationOperation兩個子類,并且可以創建繼承自NSOperation的自定義類。相比于GCD,NSOperation類更加面向對象,開發者除了不需要去了解線程相關的概念之外,甚至連GCD中需要了解的異步/同步、并行/串行都不太需要深入了解,開發者只要懂得任務和隊列即可。
1、NSOperation的子類
由于NSOperation是一個抽象類,因此不能夠直接使用NSOperation,但蘋果提供了兩個NSOperation的子類,NSBlockOperation和NSInvocationOperation,除此之外,我們還可以自定義NSOperation的子類。
二、NSOperation介紹:線程間通信
在NSOperationQueue類中,也可以獲取主線程隊列,相關更新UI的任務必須放在主隊列中完成。
下方示例中,當點擊開始下載按鈕后,會創建一個NSBlockOperation對象,放在一個普通隊列中執行,開始下載網絡圖片。此時,由于下載任務是在非主線程中進行的,所以界面上的switch按鈕是可以點擊的。等下載完成后,需要在下載任務中返回主線程去設置UI界面。
- (IBAction)startBtnAction:(id)sender {
NSBlockOperation *downloadTask = [NSBlockOperation blockOperationWithBlock:^{
//下載網絡圖片
NSString *urlString = @"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRc3Rs3zHlmPEdusO8G_I1xHyKsYUujL9ZX76nfgkVVu69oU_gosw";
NSURL *url = [NSURL URLWithString:urlString];
NSData *imageData = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:imageData];
//返回主線程設置ui
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
[mainQueue addOperationWithBlock:^{
self.imageView.image = image;
}];
}];
//創建queue
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:downloadTask];
}
三、NSOperation介紹:任務間的執行依賴
NSOperation中提供了更加方便直觀的方式來設置任務執行的先后順序關系。通過NSOperation類中的addDependency方法,即可添加任務的之間的依賴關系。由于addDependency是NSOperation類中的方法,與隊列無關,因此也可以針對不同隊列中的任務設置任務執行的先后依賴關系。
下方的示例中,有3個任務依次順序執行,先依次下載兩張圖片,圖片下載完成后,需要返回主線程去更新界面上的UIImageView,等圖片下載并刷新界面完成后,第三個任務是更新界面上的下載狀態Label。這3個任務的執行有先后依賴關系。
- (IBAction)startBtnAction:(id)sender {
//創建兩個任務,兩個任務完成后,返回主線程執行第三個更新Label的任務
NSBlockOperation *task1 = [NSBlockOperation blockOperationWithBlock:^{
//下載圖片
NSString *urlString = @"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRc3Rs3zHlmPEdusO8G_I1xHyKsYUujL9ZX76nfgkVVu69oU_gosw";
NSURL *url = [NSURL URLWithString:urlString];
NSData *imageData = [NSData dataWithContentsOfURL:url];
UIImage *image1 = [UIImage imageWithData:imageData];
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
[mainQueue addOperationWithBlock:^{
self.imageView1.image = image1;
}];
}];
NSBlockOperation *task2 = [NSBlockOperation blockOperationWithBlock:^{
//下載圖片
NSString *urlString = @"https://encrypted-tbn1.gstatic.com/images?q=tbn:ANd9GcQ6RwzhikGicc2R-tiySq7A1K8g40apnXtryI31CO3JxW7IEIUJ_Q";
NSURL *url = [NSURL URLWithString:urlString];
NSData *imageData = [NSData dataWithContentsOfURL:url];
UIImage *image2 = [UIImage imageWithData:imageData];
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
[mainQueue addOperationWithBlock:^{
self.imageView2.image = image2;
}];
}];
NSBlockOperation *task3 = [NSBlockOperation blockOperationWithBlock:^{
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
[mainQueue addOperationWithBlock:^{
self.titleLab.text = @"下載完成";
}];
}];
//設置任務之間的執行依賴關系
[task3 addDependency:task1];
[task3 addDependency:task2];
[task2 addDependency:task1];
//創建隊列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//添加任務到隊列
[queue addOperation:task1];
[queue addOperation:task2];
[queue addOperation:task3];
}
四、NSOperation與GCD的區別
- GCD是底層的C語言構成的API,而NSOperationQueue及相關對象是Objc的對象。在GCD中,在隊列中執行的是由block構成的任務,這是一個輕量級的數據結構;而NSOperation作為一個對象,為我們提供了更多的選擇;
- 在NSOperationQueue中,我們可以隨時取消已經設定要準備執行的任務(當然,已經開始的任務就無法阻止了),而GCD沒法停止已經加入queue的block(其實是有的,但需要許多復雜的代碼);
- NSOperation能夠方便地設置依賴關系,我們可以讓一個Operation依賴于另一個Operation,這樣的話盡管兩個Operation處于同一個并行隊列中,但前者會直到后者執行完畢后再執行;
我們能將KVO應用在NSOperation中,可以監聽一個Operation是否完成或取消,這樣子能比GCD更加有效地掌控我們執行的后臺任務; - 在NSOperation中,我們能夠設置NSOperation的priority優先級,能夠使同一個并行隊列中的任務區分先后地執行,而在GCD中,我們只能區分不同任務隊列的優先級,如果要區分block任務的優先級,也需要大量的復雜代碼;
- 我們能夠對NSOperation進行繼承,在這之上添加成員變量與成員方法,提高整個代碼的復用度,這比簡單地將block任務排入執行隊列更有自由度,能夠在其之上添加更多自定制的功能。