1.NSOperation的基本操做編程
使用NSOperation的兩個子類,NSInvocationOperation 和 NSBlockOperation 建立操做,而後將操做添加到隊列中去執行安全
// NSOperation // 1. 實例化 NSOperation 子類對象:NSInvocationOperation NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test1) object:nil]; // 2. 實例化 NSOperation 子類對象 NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{ // NSLog(@"耗時操做2------------"); }]; // NSOperationQueue // 1.獲取主隊列 NSOperationQueue *mainQueue = [NSOperationQueue mainQueue]; // 2.建立非主隊列 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // NSOperation使用: 將操做添加到隊列中! [mainQueue addOperation:op]; [queue addOperation:op1];
2.NSOperation定義的操做能夠直接用start啓動,至關於直接執行,入口是NSOperation中定義的main方法網絡
// 1. 實例化操做對象 NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test1) object:nil]; NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test2) object:nil]; NSInvocationOperation *op3 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test3) object:nil]; // 需求: 1. 三個操做都是耗時操做! // // 2. 建立非主隊列 // NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // // // 3. 將操做添加到非主隊列中 // [queue addOperation:op1]; // [queue addOperation:op2]; // [queue addOperation:op3]; // // 需求: 2. 三個操做都是UI操做 // NSOperationQueue *mainQueue = [NSOperationQueue mainQueue]; // // 至關於 GCD 中的 異步函數 + 主隊列! // [mainQueue addOperation:op1]; // [mainQueue addOperation:op2]; // [mainQueue addOperation:op3]; // NSOperation 執行方式2: 直接啓動!直接在當前線程執行! [op1 start]; [op2 start]; [op3 start]; // NSOperation 對象的入口是定義在自身內部的 main 方法; // 當將操做添加到操做隊列中或者直接調用操做的 start 方法以後,內部都會調用 main 方法,全部二者都可以執行操做! NSLog(@"touchesEnd"); } - (void)test1 { NSLog(@"test1----------%@",[NSThread currentThread]); } - (void)test2 { NSLog(@"test2----------%@",[NSThread currentThread]); } - (void)test3 { NSLog(@"test3----------%@",[NSThread currentThread]); }
3.使用block併發
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"222222222----%@",[NSThread currentThread]); }]; NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"33333333----%@",[NSThread currentThread]); }]; // 1.將操做添加到主隊列中 // [[NSOperationQueue mainQueue] addOperation:op1]; // [[NSOperationQueue mainQueue] addOperation:op2]; // [[NSOperationQueue mainQueue] addOperation:op3]; NSOperationQueue *queue = [[NSOperationQueue alloc] init]; [queue addOperation:op1]; [queue addOperation:op2]; [queue addOperation:op3];
4.向blockOperation中追加任務異步
// 當 NSBlockOperation中的任務數 > 1 以後, 不管是將操做添加到主線程,仍是在主線程直接執行 start ,NSBlockOperation中的任務執行順序都不肯定,執行線程也不肯定!async
// 一半在開發的時候,要避免向 NSBlockOperation 中追加任務!函數
// 若是任務都是在子線程中執行,而且不須要保證執行順序,能夠直接追加任務!atom
// 1. 實例化操做對象 NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"11111111----%@",[NSThread currentThread]); }]; // 往當前操做中追加操做 [op1 addExecutionBlock:^{ NSLog(@"追加任務1%@",[NSThread currentThread]); }]; // 往當前操做中追加操做 [op1 addExecutionBlock:^{ NSLog(@"追加任務2%@",[NSThread currentThread]); }]; // 當 NSBlockOperation中的任務數 > 1 以後, 不管是將操做添加到主線程,仍是在主線程直接執行 start ,NSBlockOperation中的任務執行順序都不肯定,執行線程也不肯定! // 一半在開發的時候,要避免向 NSBlockOperation 中追加任務! // 若是任務都是在子線程中執行,而且不須要保證執行順序,能夠直接追加任務!
5.直接經過操做隊列添加任務url
// 直接經過操做隊列添加任務! //將block中的內容當作一個操做添加到主隊列中! [[NSOperationQueue mainQueue] addOperationWithBlock:^{ NSLog(@"---------1%@",[NSThread currentThread]); }]; [[NSOperationQueue mainQueue] addOperationWithBlock:^{ NSLog(@"---------2%@",[NSThread currentThread]); }]; [[NSOperationQueue mainQueue] addOperationWithBlock:^{ NSLog(@"---------3%@",[NSThread currentThread]); }]; [[[NSOperationQueue alloc] init] addOperationWithBlock:^{ NSLog(@"---------4%@",[NSThread currentThread]); }]; [[[NSOperationQueue alloc] init] addOperationWithBlock:^{ NSLog(@"---------5%@",[NSThread currentThread]); }]; [[[NSOperationQueue alloc] init] addOperationWithBlock:^{ NSLog(@"---------6%@",[NSThread currentThread]); }];
6.給操做添加操做依賴,保證操做的順序執行,避免循環依賴spa
// NSOperation 相對於 GCD 來講,增長了如下管理線程的功能: // 1. NSOperation能夠添加操做依賴: 保證操做的執行順序! --> 和GCD中將任務添加到一個串行隊列中是同樣的! 一個串行隊列會對應一條線程! // GCD 中的按順序執行(串行隊列)----> 串行執行! // 添加操做依賴以後,系統有可能串行執行保證任務的執行順序,還有可能採用線程同步技術,保證任務執行順序! NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test) object:nil]; NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"-------222 %@",[NSThread currentThread]); }]; NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"-------333 %@",[NSThread currentThread]); }]; NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"-------444 %@",[NSThread currentThread]); }]; // 四個操做都是耗時操做! 而且要求按順序執行! 操做2是UI操做! // 添加操做依賴的注意點: // 1. 不要添加循環依賴! // 2. 必定要在將操做添加到操做隊列中以前添加操做依賴! // 優勢: 對於不一樣操做隊列中的操做,操做依賴依然有效! // 添加操做依賴! [op2 addDependency:op1]; [op3 addDependency:op2]; [op4 addDependency:op3]; // [op2 addDependency:op3]; // [op1 addDependency:op4]; // 將操做添加到操做隊列中 NSOperationQueue *quque = [[NSOperationQueue alloc] init]; [quque addOperation:op3]; [quque addOperation:op1]; [quque addOperation:op4]; // 將操做2 添加到主隊列中 [[NSOperationQueue mainQueue] addOperation:op2];
7.NSOperation的高級操做,暫停/恢復/取消/設置最大線程數
// 遇到併發編程,何時選擇 GCD ,何時選擇 NSOperation!
// 1.簡單的開啓線程/回到主線程,選擇 GCD : 效率更高,簡單!
// 2.須要管理操做(考慮到與用戶交互!),使用 NSOperation!
// NSOperation高級操做: // 1. 添加操做依賴! // 2. 管理操做: 重點! 是操做隊列的方法! // 2.1 暫停/恢復 取消 操做! // 2.2 開啓合適的線程數量!(最多不超過6條!) // 一半開發的時候,會將操做隊列設置成一個全局的變量(屬性)! // NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"0----------"); [self test]; }]; NSOperationQueue *queue = [[NSOperationQueue alloc] init]; [queue addOperationWithBlock:^{ [self test]; }]; [queue addOperation:op]; // 1. 暫停操做// 開始滾動的時候 [queue setSuspended:YES]; // 2. 恢復操做// 滾動結束的時候 [queue setSuspended:NO]; // 3. 取消全部操做// 接收到內存警告 [queue cancelAllOperations]; // 3補充: 取消單個操做!是操做的方法! [op cancel]; // 設置最大併發數,開啓合適的線程數量 // 實例化操做隊列的時候 [queue setMaxConcurrentOperationCount:6]; // 遇到併發編程,何時選擇 GCD ,何時選擇 NSOperation! // 1.簡單的開啓線程/回到主線程,選擇 GCD : 效率更高,簡單! // 2.須要管理操做(考慮到與用戶交互!),使用 NSOperation!
8.block的循環引用問題,使用__weak typeof(self)weakself = self;弱引用
#import "HMViewController.h" @interface HMViewController () // 全局操做隊列 @property(nonatomic ,strong)NSOperationQueue *queue; // @property(nonatomic ,strong)NSMutableArray *array; @end @implementation HMViewController -(NSOperationQueue *)queue { if (!_queue) { _queue = [[NSOperationQueue alloc] init]; [_queue setMaxConcurrentOperationCount:6]; } return _queue; } -(NSMutableArray *)array { if (!_array) { _array = [NSMutableArray array]; } return _array; } - (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor orangeColor]; // NSLog(@"控制器建立成功!"); } -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { NSLog(@"touchesBegan"); // block! // 1. 建立操做 // 爲了防止 block 中使用 self 出現的循環引用問題! 通常在 block中使用 self 的時候,要使用 self 的弱引用!!! // 爲了安全,block中出現self,都使用 弱引用寫法! // weakself : 下面就是 self 的弱引用寫法! __weak typeof(self)weakself = self; // __weak HMViewController *wself = self; NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{ [self test]; }]; // 將操做添加到 array 中! [self.array addObject:op]; // self --> queue -->op --> block --> self : 循環引用鏈條! // 2. 將操做添加到操做隊列中 // 由於操做執行完畢以後,操做隊列會自動釋放其中的操做,因此,在操做中(NSblockOperation)block裏有了 self,沒有關係,可以釋放.不會形成循環引用! [self.queue addOperation:op]; } -(void)test { NSLog(@"------%@",[NSThread currentThread]); } -(void)dealloc { NSLog(@"控制器銷燬了!"); }
9.線程間通訊,在子線程下載圖片,在主線程更新UI
// 1 在子線程下載圖片 NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{ // 若是圖片地址中出現了 & 符號,換一張圖片! // image :下載好的網絡圖片 UIImage *image = [self downloadWebImageWithUrlString:@"http://pic14.nipic.com/20110522/7411759_164157418126_2.jpg"]; // 回到主線程! [[NSOperationQueue mainQueue] addOperationWithBlock:^{ // 顯示圖片 self.imageView.image = image; }]; }]; [self.queue addOperation:op]; NSLog(@"touchesEnd"); } // 下載網絡圖片的方法 - (UIImage *)downloadWebImageWithUrlString:(NSString *)urlString { NSURL *url = [NSURL URLWithString:urlString]; // 下載方法!耗時方法! NSData *data = [NSData dataWithContentsOfURL:url]; UIImage *image = [UIImage imageWithData:data]; return image; }
10.自定義NSOperation,繼承NSOperation,重寫mian方法
加自動釋放池
// // HMDownloadOperation.h // 09-線程間通訊 // // Created by HM on 16/1/25. // Copyright © 2016年 HM. All rights reserved. // #import <Foundation/Foundation.h> #import <UIKit/UIKit.h> @class HMDownloadOperation; @protocol HMDownloadOperationDelegate <NSObject> -(void)downloadImageWithOperation:(HMDownloadOperation *)operation; @end @interface HMDownloadOperation : NSOperation // 代理屬性 @property(nonatomic ,weak) id<HMDownloadOperationDelegate> delegate; // 寫一個圖片屬性// 下載好的圖片 @property(nonatomic ,strong) UIImage *image; // 圖片下載地址 @property(nonatomic ,copy)NSString *urlString; @end
// // HMDownloadOperation.m // 09-線程間通訊 // // Created by HM on 16/1/25. // Copyright © 2016年 HM. All rights reserved. // #import "HMDownloadOperation.h" @implementation HMDownloadOperation // 重寫 NSOperation 的 main 方法! // 當把自定義的操做添加到操做隊列中,或者直接調用 操做的 start 方法以後,都會自動來執行 main 方法中的內容! -(void)main { // 爲了可以及時釋放內存,通常會手動書寫一個 autoreleasepool!蘋果官方文檔不要求寫! @autoreleasepool { NSLog(@"----------%@",[NSThread currentThread]); self.image = [self downloadWebImageWithUrlString:self.urlString]; NSLog(@"image:%@",self.image); // 通知/代理/block :爲了保證在不一樣對象之間傳值的準確性!採用的是同步傳值的方法! // 回到主線程,執行代理方法: dispatch_async(dispatch_get_main_queue(), ^{ // 執行代理方法 if ([self.delegate respondsToSelector:@selector(downloadImageWithOperation:)]) { [self.delegate downloadImageWithOperation:self]; } }); } } // 下載網絡圖片的方法 - (UIImage *)downloadWebImageWithUrlString:(NSString *)urlString { NSURL *url = [NSURL URLWithString:urlString]; // 下載方法!耗時方法! NSData *data = [NSData dataWithContentsOfURL:url]; UIImage *image = [UIImage imageWithData:data]; return image; } @end
11.操做完成以後的回調completionBlock
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"--------%@",[NSThread currentThread]); }]; // 操做完成以後的回調! // op.completionBlock = ^(){ // // NSLog(@"操做執行完畢!"); // // }; [op setCompletionBlock:^{ NSLog(@"操做執行完畢!"); }]; [op start];