
一、NSOperation
1.簡介
NSOperation 實例封裝了需要執(zhí)行的操作和執(zhí)行操作所需的數(shù)據(jù),并且能夠以并發(fā)或非并發(fā)的方式執(zhí)行這個操作。NSOperation 和 NSOperationQueue 實現(xiàn)多線程的具體步驟:
- 先將需要執(zhí)行的操作封裝到一個 NSOperation 對象中
- 然后將 NSOperation 對象添加到
NSOperationQueue中 - 系統(tǒng)會?動將
NSOperationQueue中的 NSOperation 取出來 - 將取出的 NSOperation 封裝的操作放到?條新線程中執(zhí)?
2.NSOperation 子類
NSOperation 是個抽象類,并不具備封裝操作的能力,必須使?它的子類
-
NSInvocationOperation(Foundation框架) -
NSBlockOperation(Foundation框架) - 自定義子類繼承 NSOperation ,實現(xiàn)內(nèi)部相應(yīng)的?法
//創(chuàng)建操作對象,封裝要執(zhí)行的 test 任務(wù)
//NSInvocationOperation 封裝操作
NSInvocationOperation *operation=[[NSInvocationOperation alloc]initWithTarget:self selector:@selector(test) object:nil];
[operation start];
【注意】默認情況下,如果操作沒有放到隊列中queue中,都是同步執(zhí)行。只有將NSOperation放到一個NSOperationQueue中,才會異步執(zhí)行操作
//能夠并發(fā)地執(zhí)行一個或多個 block 對象,所有相關(guān)的 block 都執(zhí)行完之后,操作才算完成
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^(){
NSLog(@"執(zhí)行了一個新的操作,線程:%@", [NSThread currentThread]);
}];
//通過 addExecutionBlock 方法添加一個 block 操作
[operation addExecutionBlock:^() {
NSLog(@"又執(zhí)行了1個新的操作,線程:%@", [NSThread currentThread]);
}];
// 開始執(zhí)行任務(wù)(這里還是同步執(zhí)行)
[operation start];
【注意】只要NSBlockOperation封裝的操作數(shù) > 1, 就會異步執(zhí)行操作
3.執(zhí)行操作
NSOperation 對象的 isConcurrent 方法默認返回 NO,表示操作與調(diào)用線程同步執(zhí)行,start/cancel 開始或取消線程。監(jiān)聽操作的執(zhí)行,可以通過調(diào)用 NSOperation 的 setCompletionBlock 方法來設(shè)置想做的事情
operation.completionBlock = ^() {...}; //或者
[operation setCompletionBlock:^() {...}];
二、自定義 NSOperation
【非常感謝】http://blog.csdn.net/q199109106q/article/details/8565923
1.簡介
如果NSInvocationOperation 和 NSBlockOperation 對象不能滿足需求, 你可以直接繼承 NSOperation , 并添加任何你想要的行為。繼承所需的工作量主要取決于你要實現(xiàn)非并發(fā)還是并發(fā)的 NSOperation 。
- 定義非并發(fā)的 NSOperation 要簡單許多,只需要重載
-(void)main這個方法,在這個方法里面執(zhí)行主任務(wù),并正確地響應(yīng)取消事件 - 對于并發(fā) NSOperation , 你必須重寫 NSOperation 的多個基本方法進行實現(xiàn)
2.非并發(fā)的 NSOperation:
比如叫做 DownloadOperation,用來下載圖片
1> 繼承 NSOperation,重寫 main 方法,執(zhí)行主任務(wù)
DownloadOperation.h
#import <Foundation/Foundation.h>
@protocol DownloadOperationDelegate;
@interface DownloadOperation : NSOperation
// 圖片的url路徑
@property (nonatomic, copy) NSString *imageUrl;
// 代理
@property (nonatomic, retain) id<DownloadOperationDelegate> delegate;
- (id)initWithUrl:(NSString *)url delegate:(id<DownloadOperationDelegate>)delegate;
@end
// 圖片下載的協(xié)議
@protocol DownloadOperationDelegate <NSObject>
- (void)downloadFinishWithImage:(UIImage *)image;
@end
DownloadOperation.m
#import "DownloadOperation.h"
@implementation DownloadOperation
@synthesize delegate = _delegate;
@synthesize imageUrl = _imageUrl;
// 初始化
- (id)initWithUrl:(NSString *)url delegate:(id<DownloadOperationDelegate>)delegate {
if (self = [super init]) {
self.imageUrl = url;
self.delegate = delegate;
}
return self;
}
// 釋放內(nèi)存
- (void)dealloc {
[super dealloc];
[_delegate release];
[_imageUrl release];
}
// 執(zhí)行主任務(wù)
- (void)main {
// 新建一個自動釋放池,如果是異步執(zhí)行操作,那么將無法訪問到主線程的自動釋放池
@autoreleasepool {
// ....
}
}
@end
2> 正確響應(yīng)取消事件
operation 開始執(zhí)行之后,會一直執(zhí)行任務(wù)直到完成,或者顯式地取消操作。取消可能發(fā)生在任何時候,甚至在 operation 執(zhí)行之前。盡管 NSOperation 提供了一個方法,讓應(yīng)用取消一個操作,但是識別出取消事件則是我們自己的事情。如果 operation 直接終止, 可能無法回收所有已分配的內(nèi)存或資源。因此 operation 對象需要檢測取消事件,并優(yōu)雅地退出執(zhí)行
NSOperation 對象需要定期地調(diào)用 isCancelled 方法檢測操作是否已經(jīng)被取消,如果返回 YES (表示已取消),則立即退出執(zhí)行。不管是自定義NSOperation 子類,還是使用系統(tǒng)提供的兩個具體子類,都需要支持取消。 isCancelled 方法本身非常輕量,可以頻繁地調(diào)用而不產(chǎn)生大的性能損失
以下地方可能需要調(diào)用isCancelled :
- 在執(zhí)行任何實際的工作之前
- 在循環(huán)的每次迭代過程中,如果每個迭代相對較長可能需要調(diào)用多次
- 代碼中相對比較容易中止操作的任何地方
DownloadOperation的main方法實現(xiàn)如下:
- (void)main {
// 新建一個自動釋放池,如果是異步執(zhí)行操作,那么將無法訪問到主線程的自動釋放池
@autoreleasepool {
if (self.isCancelled) return;
// 獲取圖片數(shù)據(jù)
NSURL *url = [NSURL URLWithString:self.imageUrl];
NSData *imageData = [NSData dataWithContentsOfURL:url];
if (self.isCancelled) {
url = nil;
imageData = nil;
return;
}
// 初始化圖片
UIImage *image = [UIImage imageWithData:imageData];
if (self.isCancelled) {
image = nil;
return;
}
if ([self.delegate respondsToSelector:@selector(downloadFinishWithImage:)]) {
// 把圖片數(shù)據(jù)傳回到主線程
[(NSObject *)self.delegate performSelectorOnMainThread:@selector(downloadFinishWithImage:) withObject:image waitUntilDone:NO];
}
}
}
三、NSOperationQueue
NSOperationQueue 的作?:
NSOperation 可以調(diào)? start?法來執(zhí)?任務(wù),但默認是同步執(zhí)行的,如果將NSOperation 添加到 NSOperationQueue (操作隊列)中,系統(tǒng)會自動異步執(zhí)行 NSOperation 中的操作,添加操作到 NSOperationQueue 中,自動執(zhí)行操作,自動開啟線程。
//1. 創(chuàng)建一個隊列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//2. 添加一個operation
// 第一種方法
[queue addOperation:operation];
// 第二種方法
[queue addOperationWithBlock:^() {
NSLog(@"執(zhí)行一個新的操作,線程:%@", [NSThread currentThread]);
}];
//3. 添加一組operation
[queue addOperations:operations waitUntilFinished:NO];
NSOperation添加到queue之后,通常短時間內(nèi)就會得到運行。但是如果存在依賴,或者整個queue被暫停等原因,也可能需要等待。
【注意】NSOperation添加到queue之后,絕對不要再修改NSOperation對象的狀態(tài)。因為NSOperation對象可能會在任何時候運行,因此改變NSOperation對象的依賴或數(shù)據(jù)會產(chǎn)生不利的影響。你只能查看NSOperation對象的狀態(tài), 比如是否正在運行、等待運行、已經(jīng)完成等。
四、內(nèi)容補充
1、添加NSOperation的依賴對象:
當(dāng)某個 NSOperation 對象依賴于其它 NSOperation 對象的完成時,就可以通過 addDependency 方法添加一個或者多個依賴的對象,只有所有依賴的對象都已經(jīng)完成操作,當(dāng)前 NSOperation 對象才會開始執(zhí)行操作。另外,通過 removeDependency 方法來刪除依賴對象。
[operation2 addDependency:operation1];
By the way, 優(yōu)先級不能替代依賴關(guān)系,優(yōu)先級只是對已經(jīng)準備好的 operations 確定執(zhí)行順序。先滿足依賴關(guān)系,然后再根據(jù)優(yōu)先級從所有準備好的操作中選擇優(yōu)先級最高的那個執(zhí)行。
2、 設(shè)置隊列的最大并發(fā)操作數(shù)量:
setMaxConcurrentOperationCount:方法可以配置queue的最大并發(fā)操作數(shù)量。設(shè)為1就表示queue每次只能執(zhí)行一個操作。不過operation執(zhí)行的順序仍然依賴于其它因素,比如operation是否準備好和operation的優(yōu)先級等。因此串行化的operation queue并不等同于 GCD 中的串行 dispatch queue
// 每次只能執(zhí)行一個操作
queue.maxConcurrentOperationCount = 1;
// 或者這樣寫
[queue setMaxConcurrentOperationCount:1];
3、 等待及暫停 operation :
如果需要在當(dāng)前線程中處理 operation 完成后的結(jié)果,可以使用 NSOperation 的 waitUntilFinished 方法阻塞當(dāng)前線程,等待 operation 完成。
// 會阻塞當(dāng)前線程,等到某個operation執(zhí)行完畢
[operation waitUntilFinished];
// 阻塞當(dāng)前線程,等待queue的所有操作執(zhí)行完畢
[queue waitUntilAllOperationsAreFinished];
暫停一個queue不會導(dǎo)致正在執(zhí)行的operation在任務(wù)中途暫停,只是簡單地阻止調(diào)度新Operation執(zhí)行。
// 暫停queue
[queue setSuspended:YES];
// 繼續(xù)queue
[queue setSuspended:NO];
最后,NSOperationQueue屬于多線程并發(fā)處理部分,它主要用來提供一個可添加的操作隊列,將一系列操作添加到隊列中,然后根據(jù)操作的優(yōu)先級和內(nèi)部操作依賴決定操作執(zhí)行的順序。高優(yōu)先級的操作先于低優(yōu)先級執(zhí)行。一個操作所依賴的操作全部執(zhí)行完畢后才能執(zhí)行。