一、NSOperation介紹:基本語法
NSOperation在iOS4后也基于GCD實(shí)現(xiàn),但是相對于GCD來說可控性更強(qiáng),并且可以加入操作依賴。NSOperation是一個(gè)抽象類,系統(tǒng)為我們提供了NSBlockOperation和NSInvocationOperation兩個(gè)子類,并且可以創(chuàng)建繼承自NSOperation的自定義類。相比于GCD,NSOperation類更加面向?qū)ο?,開發(fā)者除了不需要去了解線程相關(guān)的概念之外,甚至連GCD中需要了解的異步/同步、并行/串行都不太需要深入了解,開發(fā)者只要懂得任務(wù)和隊(duì)列即可。
1、NSOperation的子類
由于NSOperation是一個(gè)抽象類,因此不能夠直接使用NSOperation,但蘋果提供了兩個(gè)NSOperation的子類,NSBlockOperation和NSInvocationOperation,除此之外,我們還可以自定義NSOperation的子類。
二、NSOperation介紹:線程間通信
在NSOperationQueue類中,也可以獲取主線程隊(duì)列,相關(guān)更新UI的任務(wù)必須放在主隊(duì)列中完成。
下方示例中,當(dāng)點(diǎn)擊開始下載按鈕后,會創(chuàng)建一個(gè)NSBlockOperation對象,放在一個(gè)普通隊(duì)列中執(zhí)行,開始下載網(wǎng)絡(luò)圖片。此時(shí),由于下載任務(wù)是在非主線程中進(jìn)行的,所以界面上的switch按鈕是可以點(diǎn)擊的。等下載完成后,需要在下載任務(wù)中返回主線程去設(shè)置UI界面。

- (IBAction)startBtnAction:(id)sender {
NSBlockOperation *downloadTask = [NSBlockOperation blockOperationWithBlock:^{
//下載網(wǎng)絡(luò)圖片
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];
//返回主線程設(shè)置ui
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
[mainQueue addOperationWithBlock:^{
self.imageView.image = image;
}];
}];
//創(chuàng)建queue
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:downloadTask];
}
三、NSOperation介紹:任務(wù)間的執(zhí)行依賴
NSOperation中提供了更加方便直觀的方式來設(shè)置任務(wù)執(zhí)行的先后順序關(guān)系。通過NSOperation類中的addDependency方法,即可添加任務(wù)的之間的依賴關(guān)系。由于addDependency是NSOperation類中的方法,與隊(duì)列無關(guān),因此也可以針對不同隊(duì)列中的任務(wù)設(shè)置任務(wù)執(zhí)行的先后依賴關(guān)系。
下方的示例中,有3個(gè)任務(wù)依次順序執(zhí)行,先依次下載兩張圖片,圖片下載完成后,需要返回主線程去更新界面上的UIImageView,等圖片下載并刷新界面完成后,第三個(gè)任務(wù)是更新界面上的下載狀態(tài)Label。這3個(gè)任務(wù)的執(zhí)行有先后依賴關(guān)系。

- (IBAction)startBtnAction:(id)sender {
//創(chuàng)建兩個(gè)任務(wù),兩個(gè)任務(wù)完成后,返回主線程執(zhí)行第三個(gè)更新Label的任務(wù)
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 = @"下載完成";
}];
}];
//設(shè)置任務(wù)之間的執(zhí)行依賴關(guān)系
[task3 addDependency:task1];
[task3 addDependency:task2];
[task2 addDependency:task1];
//創(chuàng)建隊(duì)列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//添加任務(wù)到隊(duì)列
[queue addOperation:task1];
[queue addOperation:task2];
[queue addOperation:task3];
}
四、NSOperation與GCD的區(qū)別
- GCD是底層的C語言構(gòu)成的API,而NSOperationQueue及相關(guān)對象是Objc的對象。在GCD中,在隊(duì)列中執(zhí)行的是由block構(gòu)成的任務(wù),這是一個(gè)輕量級的數(shù)據(jù)結(jié)構(gòu);而NSOperation作為一個(gè)對象,為我們提供了更多的選擇;
- 在NSOperationQueue中,我們可以隨時(shí)取消已經(jīng)設(shè)定要準(zhǔn)備執(zhí)行的任務(wù)(當(dāng)然,已經(jīng)開始的任務(wù)就無法阻止了),而GCD沒法停止已經(jīng)加入queue的block(其實(shí)是有的,但需要許多復(fù)雜的代碼);
- NSOperation能夠方便地設(shè)置依賴關(guān)系,我們可以讓一個(gè)Operation依賴于另一個(gè)Operation,這樣的話盡管兩個(gè)Operation處于同一個(gè)并行隊(duì)列中,但前者會直到后者執(zhí)行完畢后再執(zhí)行;
我們能將KVO應(yīng)用在NSOperation中,可以監(jiān)聽一個(gè)Operation是否完成或取消,這樣子能比GCD更加有效地掌控我們執(zhí)行的后臺任務(wù); - 在NSOperation中,我們能夠設(shè)置NSOperation的priority優(yōu)先級,能夠使同一個(gè)并行隊(duì)列中的任務(wù)區(qū)分先后地執(zhí)行,而在GCD中,我們只能區(qū)分不同任務(wù)隊(duì)列的優(yōu)先級,如果要區(qū)分block任務(wù)的優(yōu)先級,也需要大量的復(fù)雜代碼;
- 我們能夠?qū)SOperation進(jìn)行繼承,在這之上添加成員變量與成員方法,提高整個(gè)代碼的復(fù)用度,這比簡單地將block任務(wù)排入執(zhí)行隊(duì)列更有自由度,能夠在其之上添加更多自定制的功能。