轉載自:http://blog.csdn.net/q199109106q/article/details/8565923,尊重原創!併發
NSOperation 是抽象類,對它的使用能夠直接使用系統提供的兩個子類:NSInvocationOperation、NSBlockOperation,也能夠本身寫個類,繼承NSOperation。異步
1、NSInvocationOperation函數
基於一個對象和selector來建立操做。若是你已經有現有的方法來執行須要的任務,就可使用這個類性能
(1)初始化atom
NSOperation *operation1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(testApply) object:nil]; NSOperation *operation2 = [[NSInvocationOperation alloc]initWithInvocation:<#(NSInvocation *)#>];
[operation1 start]; //開始執行,同步
2、NSBlockOperationurl
可以併發地執行一個或多個block對象,全部相關的block都執行完以後,操做纔算完成spa
(1)建立並執行添加任務.net
//建立隊列
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^(){ NSLog(@"第一個,線程id:%@", [NSThread currentThread]); }];
//添加任務 [operation addExecutionBlock:^() { NSLog(@"第二個,線程id:%@", [NSThread currentThread]); }]; [operation addExecutionBlock:^() { NSLog(@"第三個,線程id:%@", [NSThread currentThread]); }]; [operation addExecutionBlock:^() { NSLog(@"第四個,線程id:%@", [NSThread currentThread]); }]; // 開始執行任務(這裏仍是同步執行) [operation start];
打印信息以下:線程
2014-11-06 21:07:56.169 DispatchTest[16828:481571] 第三個,線程id:<NSThread: 0x7fe959638780>{number = 3, name = (null)} 2014-11-06 21:07:56.169 DispatchTest[16828:481491] 第一個,線程id:<NSThread: 0x7fe959613220>{number = 1, name = main} 2014-11-06 21:07:56.169 DispatchTest[16828:481570] 第四個,線程id:<NSThread: 0x7fe959638a00>{number = 4, name = (null)} 2014-11-06 21:07:56.169 DispatchTest[16828:481569] 第二個,線程id:<NSThread: 0x7fe95956c200>{number = 2, name = (null)}
根據number能夠看出,這幾個任務並非在同一線程被執行的,說明他們是併發執行的,且執行順序和加入隊列的順序沒有關係。代理
(2)取消操做
operation開始執行以後, 默認會一直執行操做直到完成,咱們也能夠調用cancel方法中途取消操做
[operation cancel];
(3)監聽操做的執行
若是咱們想在一個NSOperation執行完畢後作一些事情,就調用NSOperation的setCompletionBlock方法來設置想作的事情
operation.completionBlock = ^() { NSLog(@"執行完畢"); };
3、本身寫類繼承NSOperation
工做量取決於你但願你的NSOperation是併發仍是非併發。
非併發:只須要重載-(void)main這個方法,在這個方法裏面執行主任務,並正確地響應取消事件
併發: 須要重載以下4個方法
- (void)start //執行任務主函數,線程運行的入口函數 - (BOOL)isConcurrent //是否容許併發,返回YES,容許併發,返回NO不容許。默認返回NO ,便是同步的 - (BOOL)isExecuting - (BOOL)isFinished //是否已經完成,這個必需要重載,否則放在放在NSOperationQueue裏的NSOpertaion不能正常釋放
非併發示例:一個圖片下載的類好比叫作DownloadOperation,用來下載圖片
1> 繼承NSOperation,重寫main方法,執行主任務
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 // 圖片下載的協議 @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; } // 釋放內存 - (void)dealloc { [super dealloc]; [_delegate release]; [_imageUrl release]; } // 執行主任務 - (void)main { // 新建一個自動釋放池,若是是異步執行操做,那麼將沒法訪問到主線程的自動釋放池 @autoreleasepool { // .... } } @end
2> 正確響應取消事件
operation開始執行以後,會一直執行任務直到完成,或者顯式地取消操做。
取消可能發生在任什麼時候候,甚至在operation執行以前。儘管NSOperation提供了一個方法,讓應用取消一個操做,可是識別出取消事件則是咱們本身的事情。
若是operation直接終止, 可能沒法回收全部已分配的內存或資源。所以operation對象須要檢測取消事件,並優雅地退出執行
NSOperation對象須要按期地調用isCancelled方法檢測操做是否已經被取消,若是返回YES(表示已取消),則當即退出執行。
不論是自定義NSOperation子類,仍是使用系統提供的兩個具體子類,都須要支持取消。
isCancelled方法自己很是輕量,能夠頻繁地調用而不產生大的性能損失
如下地方可能須要調用isCancelled:
* 在執行任何實際的工做以前
* 在循環的每次迭代過程當中,若是每一個迭代相對較長可能須要調用屢次
* 代碼中相對比較容易停止操做的任何地方
DownloadOperation的main方法實現以下:
- (void)main { // 新建一個自動釋放池,若是是異步執行操做,那麼將沒法訪問到主線程的自動釋放池 @autoreleasepool { if (self.isCancelled) return; // 獲取圖片數據 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:)]) { // 把圖片數據傳回到主線程 [(NSObject *)self.delegate performSelectorOnMainThread:@selector(downloadFinishWithImage:) withObject:image waitUntilDone:NO]; } } }