NSOperation是OC中多線程技術(shù)的一種,是對GCD的OC包裝.它包含隊列(NSOperationQueue)
和操作(NSOperation)
兩個基本要素.
通過這篇文章你可以了解到:
- 怎樣使用NSOperation
- 怎樣使用NSOperationQueue
- 如何給NSOperationQueue設(shè)置并發(fā)數(shù)
- NSOperationQueue的暫停恢復(fù)和取消
- 通過添加依賴影響操作的執(zhí)行順序
- NSOperation的進程間通信
怎樣使用NSOperation
- NSOperation本身是一個抽象類,要使用可以通過以下幾個辦法:
- 使用NSInvocationOperation
- 使用NSBlockOperation
- 自定義NSOperation的子類
使用
- NSInvocationOperation
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task) object:nil];
// 調(diào)用start方法執(zhí)行操作op操作
[op start];
- NSBlockOperation
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"task0---%@", [NSThread currentThread]);
}];
[op start];
根據(jù)打印的結(jié)果我們會發(fā)現(xiàn),直接調(diào)用start
方法時,系統(tǒng)并不會開辟一個新的線程去執(zhí)行任務(wù),任務(wù)會在當(dāng)前線程同步執(zhí)行.
注意: 這里我們說的是當(dāng)前線程
而非主線程,意即:如果是在主線程中調(diào)用op的start方法,那么該任務(wù)是在主線程中執(zhí)行;但如果是在其他子線程調(diào)用start方法,任務(wù)則是在其他子線程執(zhí)行.
當(dāng)然NSBlockOperation還有一種使用方法addExecutionBlock:
使得我們可以給其添加更多的操作,使用如下:
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"task0---%@", [NSThread currentThread]);
}];
[op addExecutionBlock:^{
NSLog(@"task1----%@", [NSThread currentThread]);
}];
[op addExecutionBlock:^{
NSLog(@"task2----%@", [NSThread currentThread]);
}];
// 開始必須在添加其他操作之后
[op start];
由打印的結(jié)果發(fā)現(xiàn): task0的結(jié)果和前面的結(jié)論一樣,是執(zhí)行在主線程中的(因為是在主線程中調(diào)用start方法),但task1和task2都是在自己的新線程中執(zhí)行.也就是說:當(dāng)NSBlockOperation封裝的操作數(shù)大于1的時候,就會執(zhí)行異步操作.
- 自定義NSOperation
自定義NSOperation的方法也很簡單,我們需要做到下面幾個步驟:
1.子類化NSOperation
2.在.m文件里面實現(xiàn)-(void)main方法
3.初始化該操作的時候直接調(diào)用alloc及init即可
4.同樣可以通過start方法讓你自定義的任務(wù)跑在當(dāng)前線程中
關(guān)鍵代碼:
自定義NSOperation的實現(xiàn)文件:
#import "AROperation.h"
@implementation AROperation
- (void)main {
NSLog(@"this is my custom operation ---- %@", [NSThread currentThread]);
}
@end
使用(在任何需要啟用該任務(wù)的地方):
// 該操作被執(zhí)行時就會執(zhí)行op內(nèi)部定義的任務(wù)
AROperation *op = [[AROperation alloc] init];
[op start];
怎樣使用NSOperationQueue
NSOperation的start方法默認(rèn)是同步執(zhí)行任務(wù),這樣的使用并不多見,只有將NSOperation與NSOperationQueue進行結(jié)合,才會發(fā)揮出這種多線程技術(shù)的最大功效.當(dāng)NSOperation被添加到NSOperationQueue中后,就會全自動地執(zhí)行異步操作.
-
NSOperationQueue的種類:
- 自帶主隊列
[NSOperationQueue mainQueue]
: 添加到主隊列中的任務(wù)都會在主線程中執(zhí)行 - 自己創(chuàng)建隊列(非主隊列)
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
: 這種隊列同時包含串行息罗、并發(fā)的功能,添加到非主隊列的任務(wù)會自動放到子線程中執(zhí)行
- 自帶主隊列
-
向NSOperationQueue中添加操作:
- 直接添加
[queue addOperation:op1];
- 使用block添加,block的內(nèi)容會被包裝成operation對象添加到隊列
[queue addOperationWithBlock:^{ }];
操作一但被到添加到隊列中,就會自動異步執(zhí)行.
- 直接添加
設(shè)置NSOperationQueue的最大并發(fā)數(shù)
NSOperationQueue可以通過以下方法設(shè)置最大并發(fā)數(shù),
setMaxConcurrentOperationCount:
,值得注意的是:**當(dāng)并發(fā)數(shù)為1就變成了串行執(zhí)行任務(wù)
**
NSOperationQueue的暫偷嘀洌恢復(fù)和取消
- 取消
- NSOperation有一個
cancel
方法可以取消單個操作 - NSOperationQueue的
cancelAllOperations
相當(dāng)于隊列中的每個operation調(diào)用了cancel方法,會取消隊列里面全部的操作. - 但是,不能取消正在進行中的任務(wù),隊列調(diào)用了cancelAllOperations后會等當(dāng)前正在進行的任務(wù)執(zhí)行完閉后取消后面的操作
- NSOperation有一個
- 掛起和恢復(fù)
-
isSuspended
: 判斷是否掛起 -
setSuspended
: YES表示掛起,NO表示恢復(fù) - 和取消功能類似,我們同樣不能掛起正在運行中的操作,隊列會等當(dāng)前操作結(jié)束后將后面的操作暫停(掛起)
-
因此, 我們在自定義NSOperation的時候需要注意,最好可以經(jīng)常通過判斷isCancelled方法檢測操作是否被取消,以響應(yīng)外部可能進行的取消操作.如:
// 自定義NSOperation類.m文件的main方法實現(xiàn)
- (void)main {
for (NSInteger i = 0; i < 1000; i++) {
NSLog(@"SubTask---0---%zd",i);
}
// 判斷當(dāng)前任務(wù)是否被取消,如果已經(jīng)取消,及時返回
if (self.cancelled) {
return;
}
for (NSInteger i = 0; i < 1000; i++) {
NSLog(@"SubTask---1---%zd", i);
}
if (self.cancelled) {
return;
}
for (NSInteger i = 0; i < 1000; i++) {
NSLog(@"SubTask---2---%zd",i);
}
}
添加依賴和監(jiān)聽
- 通過設(shè)置操作間的依賴,可以確定這些操作的執(zhí)行順序
如:
[op3 addDependency:op1];
[op3 addDependency:op2];
表示op3會在op1和op2都執(zhí)行完畢后才執(zhí)行
添加依賴的時候要注意防止添加循環(huán)依賴,此外我們還可以在不同隊列的operation之間添加依賴
- 監(jiān)聽
-
op.completeBlock
可以監(jiān)聽一個操作執(zhí)行完畢的時刻,這個block里面可以添加一些我們需要執(zhí)行的操作 - 這個block里面的操作仍然是在子線程執(zhí)行,但不一定和被監(jiān)聽的操作在同一個線程
-
線程間通信
有時我們在子線程中執(zhí)行完一些操作的時候,需要回到主線程做一些事情(如進行UI操作),因此需要從當(dāng)前線程回到主線程,以下載并顯示圖片為例,方法如下:
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 子線程下載圖片
[queue addOperationWithBlock:^{
NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [[UIImage alloc] initWithData:data];
// 回到主線程進行顯示
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.imageView.image = image;
}];
}];
NSOperation的使用還是比較簡單的,但是要注意的細(xì)節(jié)比較多,一些方法比較容易被忽略,于是乎特此總結(jié)一下.這里就不上傳示例代碼了,有問題的童鞋可以直接留言.