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];