- 本作者在寫這篇文章之前很詳細(xì)的寫了一篇關(guān)于ios中GCD的講解,這兩篇文章可以對(duì)比著看证膨,地址為:http://www.reibang.com/p/94affa011185,也可以去我的文章列表中查找鼓黔。
1.什么是NSOperation:
NSOperation和NSOperationQueue 是蘋果提供給我們的一套多線程解決方案央勒。實(shí)際上 NSOperation和NSOperationQueue 是基于 GCD 更高一層的封裝不见,完全面向?qū)ο蟆5潜?GCD 更簡(jiǎn)單易用订歪、代碼可讀性也更高脖祈。
2.NSOperation的子類
NSOperation是一個(gè)抽象類,并不具備封裝操作的能力刷晋,必須使用它的子類盖高,使用NSOperation的子類的方式有三種:
-
NSInvocationOperation
以函數(shù)的方式封裝任務(wù) -
NSBlockOperation
以Block塊的方式封裝任務(wù) - 自定義子類繼承
NSOperation
,實(shí)現(xiàn)內(nèi)部相應(yīng)的方法眼虱。
3.NSOperation喻奥、NSOperationQueue 操作和操作隊(duì)列
既然是基于 GCD 的更高一層的封裝。那么捏悬,GCD 中的一些概念同樣適用于 NSOperation撞蚕、NSOperationQueue。在 NSOperation过牙、NSOperationQueue 中也有類似的任務(wù)(操作)和隊(duì)列(操作隊(duì)列)的概念甥厦。
3.1 操作(Operation):
- 執(zhí)行操作的意思,換句話說就是你在線程中執(zhí)行的那段代碼寇钉。
- 在 GCD 中是放在 block 中的刀疙。在 NSOperation 中,我們使用 NSOperation 子類 NSInvocationOperation扫倡、NSBlockOperation谦秧,或者自定義子類來封裝操作。
3.2 操作隊(duì)列(Operation Queues):
- 這里的隊(duì)列指操作隊(duì)列撵溃,即用來存放操作的隊(duì)列疚鲤。不同于 GCD 中的調(diào)度隊(duì)列 FIFO(先進(jìn)先出)的原則。NSOperationQueue 對(duì)于添加到隊(duì)列中的操作缘挑,首先進(jìn)入準(zhǔn)備就緒的狀態(tài)(就緒狀態(tài)取決于操作之間的依賴關(guān)系)集歇,然后進(jìn)入就緒狀態(tài)的操作的開始執(zhí)行順序(非結(jié)束執(zhí)行順序)由操作之間相對(duì)的優(yōu)先級(jí)決定(優(yōu)先級(jí)是操作對(duì)象自身的屬性)。
- 操作隊(duì)列通過設(shè)置最大并發(fā)操作數(shù)(maxConcurrentOperationCount)來控制并發(fā)语淘、串行鬼悠。
- NSOperationQueue 為我們提供了兩種不同類型的隊(duì)列:主隊(duì)列和自定義隊(duì)列。其中自定義隊(duì)列默認(rèn)為并發(fā)隊(duì)列亏娜,但是可以通過控制來變成一個(gè)串行隊(duì)列,主隊(duì)列為串行隊(duì)列蹬挺,運(yùn)行在主線程之上维贺,而自定義隊(duì)列在后臺(tái)執(zhí)行。
4.NSOperation和NSOperationQueue實(shí)現(xiàn)多線程的具體步驟
- 先將需要執(zhí)行的操作封裝到一個(gè)NSOperation中
- 然后將NSOperation對(duì)象添加到NSOperationQueue中
- 系統(tǒng)會(huì)自動(dòng)將NSOperationQueue中的NSOperation取出來
- 將取出來的NSOperation封裝的操作放到一條新線程中執(zhí)行
//簡(jiǎn)單添加任務(wù)
/*
輸入結(jié)果
2019-03-04 15:44:05.880348+0800 SmallProgram[4075:178369] 1---<NSThread: 0x600000ece7c0>{number = 1, name = main}
2019-03-04 15:44:05.880541+0800 SmallProgram[4075:178369] 2---<NSThread: 0x600000ece7c0>{number = 1, name = main}
2019-03-04 15:44:05.880711+0800 SmallProgram[4075:178369] 3---<NSThread: 0x600000ece7c0>{number = 1, name = main}
2019-03-04 15:44:05.880736+0800 SmallProgram[4075:178504] 4---<NSThread: 0x600000e68b00>{number = 7, name = (null)}
2019-03-04 15:44:05.880736+0800 SmallProgram[4075:178482] 5---<NSThread: 0x600000e68140>{number = 8, name = (null)}
*/
-(void)blockOperation{
//01 --封裝操作對(duì)象
NSBlockOperation * op1 = [NSBlockOperation blockOperationWithBlock:^{
//任務(wù)01
NSLog(@"1---%@",[NSThread currentThread]);
}];
NSBlockOperation * op2 = [NSBlockOperation blockOperationWithBlock:^{
//任務(wù)02
NSLog(@"2---%@",[NSThread currentThread]);
}];
NSBlockOperation * op3 = [NSBlockOperation blockOperationWithBlock:^{
//任務(wù)03
NSLog(@"3---%@",[NSThread currentThread]);
}];
//追加任務(wù)
//當(dāng)一個(gè)操作中的任務(wù)數(shù)量大于1的時(shí)候巴帮,就會(huì)開啟子線程和當(dāng)前線程一起執(zhí)行任務(wù)
[op3 addExecutionBlock:^{
NSLog(@"4---%@",[NSThread currentThread]);
}];
[op3 addExecutionBlock:^{
NSLog(@"5---%@",[NSThread currentThread]);
}];
//02--執(zhí)行操作
[op1 start];
[op2 start];
[op3 start];
}
5.操作隊(duì)列NSOperationQueue的基本使用
-(void)blockOperationQueue{
//01 --創(chuàng)建隊(duì)列
NSOperationQueue * queue = [[NSOperationQueue alloc] init];
//02 --封裝操作對(duì)象
NSBlockOperation * op1 = [NSBlockOperation blockOperationWithBlock:^{
//任務(wù)01
NSLog(@"1---%@",[NSThread currentThread]);
}];
NSBlockOperation * op2 = [NSBlockOperation blockOperationWithBlock:^{
//任務(wù)02
NSLog(@"2---%@",[NSThread currentThread]);
}];
NSBlockOperation * op3 = [NSBlockOperation blockOperationWithBlock:^{
//任務(wù)03
NSLog(@"3---%@",[NSThread currentThread]);
}];
//03 --把操作添加到隊(duì)列中
[queue addOperation:op1]; //該方法內(nèi)部會(huì)自動(dòng)調(diào)用start方法執(zhí)行任務(wù)
[queue addOperation:op2];
[queue addOperation:op3];
/*
//簡(jiǎn)便方法:該方法n首先會(huì)吧block中任務(wù)封裝成一個(gè)操作溯泣,然后把該操作直接添加到隊(duì)列中
[queue addOperationWithBlock:^{
NSLog(@"4---%@",[NSThread currentThread]);
}];
*/
}
6.設(shè)置maxConcurrentOperationCount
來改為串行隊(duì)列
-(void)changeSerialQueue{
//01 --創(chuàng)建隊(duì)列
NSOperationQueue * queue = [[NSOperationQueue alloc] init];
//02 --封裝操作對(duì)象
NSBlockOperation * op1 = [NSBlockOperation blockOperationWithBlock:^{
//任務(wù)01
NSLog(@"1---%@",[NSThread currentThread]);
}];
NSBlockOperation * op2 = [NSBlockOperation blockOperationWithBlock:^{
//任務(wù)02
NSLog(@"2---%@",[NSThread currentThread]);
}];
NSBlockOperation * op3 = [NSBlockOperation blockOperationWithBlock:^{
//任務(wù)03
NSLog(@"3---%@",[NSThread currentThread]);
}];
NSBlockOperation * op4 = [NSBlockOperation blockOperationWithBlock:^{
//任務(wù)03
NSLog(@"4---%@",[NSThread currentThread]);
}];
NSBlockOperation * op5 = [NSBlockOperation blockOperationWithBlock:^{
//任務(wù)03
NSLog(@"5---%@",[NSThread currentThread]);
}];
//設(shè)置最大并發(fā)數(shù)--同一時(shí)間最多有多少調(diào)線程執(zhí)行
//maxConcurrentOperationCount ==0 不執(zhí)行任務(wù)
//maxConcurrentOperationCount默認(rèn)為-1虐秋,指一個(gè)最大的值
queue.maxConcurrentOperationCount = 1;
//03 --把操作添加到隊(duì)列中
[queue addOperation:op1]; //該方法內(nèi)部會(huì)自動(dòng)調(diào)用start方法執(zhí)行任務(wù)
[queue addOperation:op2];
[queue addOperation:op3];
[queue addOperation:op4];
[queue addOperation:op5];
}
7.操作隊(duì)列的暫停、回復(fù)和取消功能
//暫停
//只能暫停當(dāng)前操作后面的操作垃沦,當(dāng)前操作不可分割必須執(zhí)行完畢
[queue setSuspended:YES];
//恢復(fù)
[queue setSuspended:NO];
//取消
//智能取消隊(duì)列中處理等待狀態(tài)的操作
[queue cancelAllOperations];
8.自定義操作
//.h文件
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface MYNSOperation : NSOperation
@end
NS_ASSUME_NONNULL_END
//.m文件
#import "MYNSOperation.h"
@implementation MYNSOperation
//重寫main方法來告訴自定義的操作任務(wù)是什么
//好處及其使用:比如工程中多次利用到圖片下載客给,就可以將下載功能放到main方法中,以后調(diào)用就直接alloc就可以了
-(void)main{
if(self.isCancelled){
return;
}
//執(zhí)行的任務(wù)
for(int i=0;i<5;i++){
NSLog(@"%d--%@",i,[NSThread currentThread]);
}
}
@end
//使用方法
NSOperationQueue * queue = [[NSOperationQueue alloc]init];
MYNSOperation * op = [[MYNSOperation alloc]init];
[queue addOperation:op]; //內(nèi)容會(huì)調(diào)用start--->main
9.操作的依賴和監(jiān)聽
/*
輸出結(jié)果:
2019-03-04 17:14:04.382909+0800 SmallProgram[5396:240448] 4---<NSThread: 0x600001e9c000>{number = 3, name = (null)}
2019-03-04 17:14:04.383237+0800 SmallProgram[5396:240448] 電影已經(jīng)下載好了肢簿,可以觀看了
2019-03-04 17:14:04.383261+0800 SmallProgram[5396:240470] 3---<NSThread: 0x600001e59b80>{number = 9, name = (null)}
2019-03-04 17:14:04.383440+0800 SmallProgram[5396:240470] 2---<NSThread: 0x600001e59b80>{number = 9, name = (null)}
2019-03-04 17:14:04.383712+0800 SmallProgram[5396:240448] 1---<NSThread: 0x600001e9c000>{number = 3, name = (null)}
2019-03-04 17:14:04.383955+0800 SmallProgram[5396:240448] 5---<NSThread: 0x600001e9c000>{number = 3, name = (null)}
*/
-(void)relyOn{
//01 --創(chuàng)建隊(duì)列
NSOperationQueue * queue = [[NSOperationQueue alloc] init];
NSOperationQueue * queue2 = [[NSOperationQueue alloc] init];
//02 --封裝操作對(duì)象
NSBlockOperation * op1 = [NSBlockOperation blockOperationWithBlock:^{
//任務(wù)01
NSLog(@"1---%@",[NSThread currentThread]);
}];
NSBlockOperation * op2 = [NSBlockOperation blockOperationWithBlock:^{
//任務(wù)02
NSLog(@"2---%@",[NSThread currentThread]);
}];
NSBlockOperation * op3 = [NSBlockOperation blockOperationWithBlock:^{
//任務(wù)03
NSLog(@"3---%@",[NSThread currentThread]);
}];
NSBlockOperation * op4 = [NSBlockOperation blockOperationWithBlock:^{
//任務(wù)03
NSLog(@"4---%@",[NSThread currentThread]);
}];
NSBlockOperation * op5 = [NSBlockOperation blockOperationWithBlock:^{
//任務(wù)03
NSLog(@"5---%@",[NSThread currentThread]);
}];
//監(jiān)聽任務(wù)執(zhí)行完畢
op4.completionBlock = ^{
NSLog(@"電影已經(jīng)下載好了靶剑,可以觀看了");
};
//03設(shè)置依賴4-->3-->2-->1-->5
//警告:不能設(shè)置循環(huán)依賴,否則死鎖
[op5 addDependency:op1];
[op1 addDependency:op2];
[op2 addDependency:op3];
[op3 addDependency:op4];
//04 --把操作添加到隊(duì)列中
[queue addOperation:op1]; //該方法內(nèi)部會(huì)自動(dòng)調(diào)用start方法執(zhí)行任務(wù)
[queue addOperation:op2];
[queue addOperation:op3];
[queue addOperation:op4];
[queue2 addOperation:op5];
}
10.操作隊(duì)列實(shí)現(xiàn)線程間通信(模仿子線程下載圖片池充,然后主線程刷新UI)
/*
輸出結(jié)果:
下載中--<NSThread: 0x600001328bc0>{number = 8, name = (null)}
2019-03-04 17:24:28.905424+0800 SmallProgram[5558:245680] 刷新UI--<NSThread: 0x600001389840>{number = 1, name = main}
*/
-(void)download{
//01 --創(chuàng)建隊(duì)列
NSOperationQueue * queue = [[NSOperationQueue alloc] init];
//02 --封裝操作對(duì)象
NSBlockOperation * download = [NSBlockOperation blockOperationWithBlock:^{
//01--下載
NSLog(@"下載中--%@",[NSThread currentThread]);
//02--UI
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
NSLog(@"刷新UI--%@",[NSThread currentThread]);
}];
}];
[queue addOperation:download];
}
11.NSOperation桩引、NSOperationQueue 線程安全(模仿賣火車票)
- (void)ticketStatusSave {
self.ticketCount = 100; //模擬總共100張票
self.lock = [[NSLock alloc] init]; //初始化鎖
// 01.創(chuàng)建 queue1,模擬窗口1
NSOperationQueue *queue1 = [[NSOperationQueue alloc] init];
queue1.maxConcurrentOperationCount = 1;
// 02.創(chuàng)建 queue2,模擬窗口2
NSOperationQueue *queue2 = [[NSOperationQueue alloc] init];
queue2.maxConcurrentOperationCount = 1;
// 03.添加到隊(duì)列,然后開始賣票
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
[self saleTicketSafe];
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
[self saleTicketSafe];
}];
[queue1 addOperation:op1];
[queue2 addOperation:op2];
}
/**
* 售賣火車票(線程安全)
*/
- (void)saleTicketSafe {
while (1) {
// 加鎖
[self.lock lock];
if (self.ticketCount > 0) {
//如果還有票收夸,繼續(xù)售賣
self.ticketCount--;
NSLog(@"%@", [NSString stringWithFormat:@"剩余票數(shù):%zd 窗口:%@", self.ticketCount, [NSThread currentThread]]);
[NSThread sleepForTimeInterval:0.2];
}
// 解鎖
[self.lock unlock];
if (self.ticketCount <= 0) {
NSLog(@"所有火車票均已售完");
break;
}
}
}
12.GCD和NSOperation的比較
12.1 GCD:
- GCD是純C語(yǔ)言的API坑匠,而操作隊(duì)列則是Object-C對(duì)象。
- 在GCD中卧惜,任務(wù)塊(Block)來表示厘灼,而塊是個(gè)輕量級(jí)的數(shù)據(jù)結(jié)構(gòu),相反操作隊(duì)列中的NSOperation則是個(gè)更加重量級(jí)的Object-C對(duì)象咽瓷。
- 具體該使用哪種還得具體情況分析设凹。
12.2 NSOperation和NSOperationQueue的優(yōu)勢(shì):
- NSOperation和NSOperationQueue可以方便的調(diào)用cancel方法來取消某個(gè)操作,而GCD中的任務(wù)是無法被取消的(安排好任務(wù)之后就不管了)忱详。
- NSOperation可以方便的指定操作間的依賴關(guān)系围来。
- NSOperation可以通過KVO提供對(duì)NSOperation對(duì)象的精細(xì)控制(如監(jiān)聽當(dāng)前操作是否被取消或者是否已經(jīng)完成)
- NSOperation可以方便的設(shè)置操作的優(yōu)先級(jí)(
qualityOfService
),操作優(yōu)先級(jí)表示此操作與隊(duì)列中其它操作之間的優(yōu)先關(guān)系匈睁,優(yōu)先級(jí)高德操作先執(zhí)行监透。 - 通過自定義的NSOperation的子類可以實(shí)現(xiàn)操作重用。
結(jié)尾
本文參考:
iOS 多線程:『NSOperation航唆、NSOperationQueue』詳盡總結(jié)http://www.reibang.com/p/4b1d77054b35