NSOperation和NSOperationQueue
NSOperation
是蘋(píng)果公司對(duì) GCD
的封裝梅猿,完全面向?qū)ο蟛郯溃允褂闷饋?lái)更好理解爷辱。 大家可以看到 NSOperation
和 NSOperationQueue
分別對(duì)應(yīng) GCD
的 任務(wù) 和 隊(duì)列 鳄乏。操作步驟也很好理解:
- 將要執(zhí)行的任務(wù)封裝到一個(gè)
NSOperation
對(duì)象中唠亚。 - 將此任務(wù)添加到一個(gè)
NSOperationQueue
對(duì)象中蚪战。
然后系統(tǒng)就會(huì)自動(dòng)在執(zhí)行任務(wù)牵现。至于同步還是異步、串行還是并行請(qǐng)繼續(xù)往下看:
添加任務(wù)
值得說(shuō)明的是邀桑,NSOperation
只是一個(gè)抽象類(lèi)瞎疼,所以不能封裝任務(wù)。但它有 2 個(gè)子類(lèi)用于封裝任務(wù)壁畸。分別是:NSInvocationOperation
和 NSBlockOperation
贼急。創(chuàng)建一個(gè) Operation
后,需要調(diào)用 start
方法來(lái)啟動(dòng)任務(wù)捏萍,它會(huì) 默認(rèn)在當(dāng)前隊(duì)列同步執(zhí)行太抓。當(dāng)然你也可以在中途取消一個(gè)任務(wù),只需要調(diào)用其 cancel
方法即可令杈。
-
NSInvocationOperation
: 需要傳入一個(gè)方法名走敌。
OBJECTIVE-C
//1.創(chuàng)建NSInvocationOperation對(duì)象
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
//2.開(kāi)始執(zhí)行
[operation start];
SWIFT
在 `Swift` 構(gòu)建的和諧社會(huì)里,是容不下 NSInvocationOperation 這種不是類(lèi)型安全的敗類(lèi)的逗噩。蘋(píng)果如是說(shuō)悔常。這里有[相關(guān)解釋](http://stackoverflow.com/questions/26644477/nsinvocationoperation-is-unavailable-in-xcode-6-1)
-
NSBlockOperation
OBJECTIVE-C
//1.創(chuàng)建NSBlockOperation對(duì)象
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%@", [NSThread currentThread]);
}];
//2.開(kāi)始任務(wù)
[operation start];
SWIFT
//1.創(chuàng)建NSBlockOperation對(duì)象
let operation = NSBlockOperation { () -> Void in
println(NSThread.currentThread())
}
//2.開(kāi)始任務(wù)
operation.start()
之前說(shuō)過(guò)這樣的任務(wù),默認(rèn)會(huì)在當(dāng)前線程執(zhí)行给赞。但是 `NSBlockOperation` 還有一個(gè)方法:`addExecutionBlock:` 机打,通過(guò)這個(gè)方法可以給 `Operation` 添加多個(gè)執(zhí)行 `Block`。這樣 `Operation` 中的任務(wù) 會(huì)**并發(fā)執(zhí)行**片迅,它會(huì) 在**主線程**和**其它的多個(gè)線程** 執(zhí)行這些任務(wù)残邀,注意下面的打印結(jié)果:
OBJECTIVE-C
//1.創(chuàng)建NSBlockOperation對(duì)象
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%@", [NSThread currentThread]);
}];
//添加多個(gè)Block
for (NSInteger i = 0; i < 5; i++) {
[operation addExecutionBlock:^{
NSLog(@"第%ld次:%@", i, [NSThread currentThread]);
}];
}
//2.開(kāi)始任務(wù)
[operation start];
SWIFT
//1.創(chuàng)建NSBlockOperation對(duì)象
let operation = NSBlockOperation { () -> Void in
NSLog("%@", NSThread.currentThread())
}
//2.添加多個(gè)Block
for i in 0..<5 {
operation.addExecutionBlock { () -> Void in
NSLog("第%ld次 - %@", i, NSThread.currentThread())
}
}
//2.開(kāi)始任務(wù)
operation.start()
打印輸出
2017-04-11 17:50:16.585 test[17527:4095467] 第2次 - <NSThread: 0x7ff5c9701910>{number = 1, name = main}
2017-04-11 17:50:16.585 test[17527:4095666] 第1次 - <NSThread: 0x7ff5c972caf0>{number = 4, name = (null)}
2017-04-11 17:50:16.585 test[17527:4095665] <NSThread: 0x7ff5c961b610>{number = 3, name = (null)}
2017-04-11 17:50:16.585 test[17527:4095662] 第0次 - <NSThread: 0x7ff5c948d310>{number = 2, name = (null)}
2017-04-11 17:50:16.586 test[17527:4095666] 第3次 - <NSThread: 0x7ff5c972caf0>{number = 4, name = (null)}
2017-04-11 17:50:16.586 test[17527:4095467] 第4次 - <NSThread: 0x7ff5c9701910>{number = 1, name = main}
NOTE:addExecutionBlock
方法必須在 start()
方法之前執(zhí)行,否則就會(huì)報(bào)錯(cuò):
‘*** -[NSBlockOperation addExecutionBlock:]: blocks cannot be added after the operation has started executing or finished'
-
自定義Operation
除了上面的兩種
Operation
以外柑蛇,我們還可以自定義Operation
芥挣。自定義Operation
需要繼承NSOperation
類(lèi),并實(shí)現(xiàn)其main()
方法耻台,因?yàn)樵谡{(diào)用start()
方法的時(shí)候空免,內(nèi)部會(huì)調(diào)用main()
方法完成相關(guān)邏輯。所以如果以上的兩個(gè)類(lèi)無(wú)法滿足你的欲望的時(shí)候盆耽,你就需要自定義了蹋砚。你想要實(shí)現(xiàn)什么功能都可以寫(xiě)在里面扼菠。除此之外,你還需要實(shí)現(xiàn)cancel()
在內(nèi)的各種方法坝咐。
創(chuàng)建隊(duì)列
看過(guò)上面的內(nèi)容就知道循榆,我們可以調(diào)用一個(gè) NSOperation
對(duì)象的 start()
方法來(lái)啟動(dòng)這個(gè)任務(wù),但是這樣做他們默認(rèn)是 同步執(zhí)行 的墨坚。就算是 addExecutionBlock
方法秧饮,也會(huì)在 當(dāng)前線程和其他線程 中執(zhí)行,也就是說(shuō)還是會(huì)占用當(dāng)前線程泽篮。這是就要用到隊(duì)列 NSOperationQueue
了盗尸。而且,按類(lèi)型來(lái)說(shuō)的話一共有兩種類(lèi)型:主隊(duì)列帽撑、其他隊(duì)列振劳。只要添加到隊(duì)列,會(huì)自動(dòng)調(diào)用任務(wù)的 start()
方法
-
主隊(duì)列
細(xì)心的同學(xué)就會(huì)發(fā)現(xiàn)油狂,每套多線程方案都會(huì)有一個(gè)主線程(當(dāng)然啦历恐,說(shuō)的是iOS中,像pthread
這種多系統(tǒng)的方案并沒(méi)有专筷,因?yàn)?UI線程 理論需要每種操作系統(tǒng)自己定制)弱贼。這是一個(gè)特殊的線程,必須串行磷蛹。所以添加到主隊(duì)列的任務(wù)都會(huì)一個(gè)接一個(gè)地排著隊(duì)在主線程處理吮旅。
//OBJECTIVE-C
NSOperationQueue *queue = [NSOperationQueue mainQueue];
//SWIFT
let queue = NSOperationQueue.mainQueue()
-
其他隊(duì)列
因?yàn)橹麝?duì)列比較特殊,所以會(huì)單獨(dú)有一個(gè)類(lèi)方法來(lái)獲得主隊(duì)列味咳。那么通過(guò)初始化產(chǎn)生的隊(duì)列就是其他隊(duì)列了庇勃,因?yàn)橹挥羞@兩種隊(duì)列,除了主隊(duì)列槽驶,其他隊(duì)列就不需要名字了责嚷。注意:其他隊(duì)列的任務(wù)會(huì)在其他線程并行執(zhí)行。
OBJECTIVE-C
//1.創(chuàng)建一個(gè)其他隊(duì)列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//2.創(chuàng)建NSBlockOperation對(duì)象
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%@", [NSThread currentThread]);
}];
//3.添加多個(gè)Block
for (NSInteger i = 0; i < 5; i++) {
[operation addExecutionBlock:^{
NSLog(@"第%ld次:%@", i, [NSThread currentThread]);
}];
}
//4.隊(duì)列添加任務(wù)
[queue addOperation:operation];
SWIFT
//1.創(chuàng)建其他隊(duì)列
let queue = NSOperationQueue()
//2.創(chuàng)建NSBlockOperation對(duì)象
let operation = NSBlockOperation { () -> Void in
NSLog("%@", NSThread.currentThread())
}
//3.添加多個(gè)Block
for i in 0..<5 {
operation.addExecutionBlock { () -> Void in
NSLog("第%ld次 - %@", i, NSThread.currentThread())
}
}
//4.隊(duì)列添加任務(wù)
queue.addOperation(operation)
打印輸出
2017-04-11 20:26:28.463 test[18622:4443534] <NSThread: 0x7fd022c3ac10>{number = 5, name = (null)}
2017-04-11 20:26:28.463 test[18622:4443536] 第2次 - <NSThread: 0x7fd022e36d50>{number = 2, name = (null)}
2017-04-11 20:26:28.463 test[18622:4443535] 第0次 - <NSThread: 0x7fd022f237f0>{number = 4, name = (null)}
2017-04-11 20:26:28.463 test[18622:4443533] 第1次 - <NSThread: 0x7fd022d372b0>{number = 3, name = (null)}
2017-04-11 20:26:28.463 test[18622:4443534] 第3次 - <NSThread: 0x7fd022c3ac10>{number = 5, name = (null)}
2017-04-11 20:26:28.463 test[18622:4443536] 第4次 - <NSThread: 0x7fd022e36d50>{number = 2, name = (null)}
OK, 這時(shí)應(yīng)該發(fā)問(wèn)了掂铐,大家將 NSOperationQueue
與 GCD
的隊(duì)列 相比較就會(huì)發(fā)現(xiàn)罕拂,這里沒(méi)有串行隊(duì)列,那如果我想要10個(gè)任務(wù)在其他線程串行的執(zhí)行怎么辦全陨?
這就是蘋(píng)果封裝的妙處爆班,你不用管串行、并行辱姨、同步柿菩、異步這些名詞。NSOperationQueue
有一個(gè)參數(shù) maxConcurrentOperationCount
最大并發(fā)數(shù)雨涛,用來(lái)設(shè)置最多可以讓多少個(gè)任務(wù)同時(shí)執(zhí)行枢舶。當(dāng)你把它設(shè)置為 1
的時(shí)候懦胞,他不就是串行了嘛!
NSOperationQueue
還有一個(gè)添加任務(wù)的方法祟辟,- (void)addOperationWithBlock:(void (^)(void))block;
医瘫,這是不是和 GCD
差不多侣肄?這樣就可以添加一個(gè)任務(wù)到隊(duì)列中了旧困,十分方便。
NSOperation
有一個(gè)非常實(shí)用的功能稼锅,那就是添加依賴(lài)吼具。比如有 3 個(gè)任務(wù):A: 從服務(wù)器上下載一張圖片,B:給這張圖片加個(gè)水印矩距,C:把圖片返回給服務(wù)器拗盒。這時(shí)就可以用到依賴(lài)了:
OBJECTIVE-C
//1.任務(wù)一:下載圖片
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"下載圖片 - %@", [NSThread currentThread]);
[NSThread sleepForTimeInterval:1.0];
}];
//2.任務(wù)二:打水印
NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"打水印 - %@", [NSThread currentThread]);
[NSThread sleepForTimeInterval:1.0];
}];
//3.任務(wù)三:上傳圖片
NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"上傳圖片 - %@", [NSThread currentThread]);
[NSThread sleepForTimeInterval:1.0];
}];
//4.設(shè)置依賴(lài)
[operation2 addDependency:operation1]; //任務(wù)二依賴(lài)任務(wù)一
[operation3 addDependency:operation2]; //任務(wù)三依賴(lài)任務(wù)二
//5.創(chuàng)建隊(duì)列并加入任務(wù)
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperations:@[operation3, operation2, operation1] waitUntilFinished:NO];
SWIFT
//1.任務(wù)一:下載圖片
let operation1 = NSBlockOperation { () -> Void in
NSLog("下載圖片 - %@", NSThread.currentThread())
NSThread.sleepForTimeInterval(1.0)
}
//2.任務(wù)二:打水印
let operation2 = NSBlockOperation { () -> Void in
NSLog("打水印 - %@", NSThread.currentThread())
NSThread.sleepForTimeInterval(1.0)
}
//3.任務(wù)三:上傳圖片
let operation3 = NSBlockOperation { () -> Void in
NSLog("上傳圖片 - %@", NSThread.currentThread())
NSThread.sleepForTimeInterval(1.0)
}
//4.設(shè)置依賴(lài)
operation2.addDependency(operation1) //任務(wù)二依賴(lài)任務(wù)一
operation3.addDependency(operation2) //任務(wù)三依賴(lài)任務(wù)二
//5.創(chuàng)建隊(duì)列并加入任務(wù)
let queue = NSOperationQueue()
queue.addOperations([operation3, operation2, operation1], waitUntilFinished: false)
打印結(jié)果
2017-04-11 21:24:28.622 test[19392:4637517] 下載圖片 - <NSThread: 0x7fc10ad4d970>{number = 2, name = (null)}
2017-04-11 21:24:29.622 test[19392:4637515] 打水印 - <NSThread: 0x7fc10af20ef0>{number = 3, name = (null)}
2017-04-11 21:24:30.627 test[19392:4637515] 上傳圖片 - <NSThread: 0x7fc10af20ef0>{number = 3, name = (null)}
注意:
- 不能添加相互依賴(lài),會(huì)死鎖锥债,比如 A依賴(lài)B陡蝇,B依賴(lài)A。
- 可以使用
removeDependency
來(lái)解除依賴(lài)關(guān)系哮肚。 - 可以在不同的隊(duì)列之間依賴(lài)登夫,反正就是這個(gè)依賴(lài)是添加到任務(wù)身上的,和隊(duì)列沒(méi)關(guān)系允趟。
其他方法
以上就是一些主要方法, 下面還有一些常用方法需要大家注意:
NSOperation
BOOL executing; //判斷任務(wù)是否正在執(zhí)行
BOOL finished; //判斷任務(wù)是否完成
void (^completionBlock)(void); //用來(lái)設(shè)置完成后需要執(zhí)行的操作
- (void)cancel; //取消任務(wù)
- (void)waitUntilFinished; //阻塞當(dāng)前線程直到此任務(wù)執(zhí)行完畢
NSOperationQueue
NSUInteger operationCount; //獲取隊(duì)列的任務(wù)數(shù)
- (void)cancelAllOperations; //取消隊(duì)列中所有的任務(wù)
- (void)waitUntilAllOperationsAreFinished; //阻塞當(dāng)前線程直到此隊(duì)列中的所有任務(wù)執(zhí)行完畢
[queue setSuspended:YES]; // 暫停queue
[queue setSuspended:NO]; // 繼續(xù)queue