ios学习笔记——操作队列 NSOperation
NSOperation是一个抽象类,我们不能直接使用。当我们使用NSOperation时有以下三种方式:
1、NSInvocationOperation:NSOperation的子类,可以在使用该类的情况下使用已有方法来执行所需的任务。
2、NSBlockOperation:NSOperation子类,同时执行一个或多个块对象。
3、创建自己的NSOperation子类:
所有的NSOperation对象支持以下主要功能:
1、创建对象之间的依赖
2、当operation的主要任务完成后,支持一个可选的block
3、你可以使用KVO通知来监测operation的执行状态的改变
4、设置operation的优先级
5、支持取消,允许在执行时停止操作。
使用NSInvocationOperation对象
1 #import "ViewController.h" 2 3 @interface ViewController () 4 5 @end 6 7 @implementation ViewController 8 9 - (void)viewDidLoad { 10 [super viewDidLoad]; 11 12 NSString * data = @"shaojiazuo"; 13 NSInvocationOperation * operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test:) object:data]; 14 15 //启动opeartion 16 [operation start]; 17 } 18 19 /** 20 * operation回掉函数 21 * 22 * @param data opeartaion创建的时候传过来的数据 23 */ 24 - (void)test:(NSString *)data 25 { 26 NSLog(@"%@", data); 27 } 28 29 @end
本例简单使用了NSInvocationOperation对象,调用start方法,可以启动operation,但是不会开启新线程。
输出:
使用NSBlockOperation
1 NSBlockOperation * operation = [NSBlockOperation blockOperationWithBlock:^{ 2 NSThread * thread = [NSThread currentThread]; 3 NSLog(@"%@", thread); 4 }]; 5 6 [operation start];
输出结果:
创建块操作对象后,我们还可以使用addExecutionBlock:方法,添加更多的块。
1 NSBlockOperation * operation = [NSBlockOperation blockOperationWithBlock:^{ 2 NSThread * thread = [NSThread currentThread]; 3 NSLog(@"1--%@", thread); 4 }]; 5 6 [operation addExecutionBlock:^{ 7 NSThread * thread = [NSThread currentThread]; 8 NSLog(@"2--%@", thread); 9 }]; 10 11 [operation start];
输出结果:
我们只使用一个block操作的时候,在主线程中执行,不会开始新的线程。一但我们使用了addExecutionBlock:方法,添加了跟多的块的时候,就会自动开启新的线程执行新添加的块代码。
自定义Operation对象
如果感觉NSInvocationOperation和NSBlockOperation不适用,那么我们可以自定义operation。
实现的工作量取决于你要实现非并发还是并发的NSOpeartion。定义非并发的NSOperation要简单许多,只要重载-(void)main这个方法,在这里面执行主要任务,并正确的响应取消事件。并发NSOperation,必须重写多个基本方法进行实现。
非并发操作
sjzOperation.h
1 #import <Foundation/Foundation.h> 2 #import <UIKit/UIKit.h> 3 4 @protocol sjzOperationDeleage <NSObject> 5 6 - (void)downloadFinish:(UIImage *)image; 7 8 @end 9 10 @interface sjzOperation : NSOperation 11 12 @property (nonatomic, strong) NSString * imageUrl; 13 @property (nonatomic, weak) id<sjzOperationDeleage> deleage; 14 15 - (instancetype)initWithURL:(NSString *)imageUrl; 16 17 @end
sjzOperation.m
1 #import "sjzOperation.h" 2 3 @implementation sjzOperation 4 5 - (instancetype)initWithURL:(NSString *)imageUrl 6 { 7 self = [super init]; 8 if(self){ 9 self.imageUrl = imageUrl; 10 } 11 12 return self; 13 } 14 15 - (void)main 16 { 17 //如果是异步执行操作,那么将无法访问到主线程的自动释放池 18 @autoreleasepool { 19 //线程可能被取消,所以要判断 20 if(self.isCancelled){ 21 return; 22 } 23 24 NSData * data = [NSData dataWithContentsOfURL:[NSURL URLWithString:self.imageUrl]]; 25 26 //在执行比较耗时的操作时,要判断是否取消线程 27 if(self.isCancelled){ 28 return; 29 } 30 31 UIImage * image = [UIImage imageWithData:data]; 32 33 if(self.isCancelled){ 34 return; 35 } 36 37 if([self.deleage respondsToSelector:@selector(downloadFinish:)]){ 38 //更改UI要到主线程中 39 [self.deleage performSelector:@selector(downloadFinish:) withObject:image]; 40 } 41 42 } 43 } 44 45 @end
调用
1 #import "ViewController.h" 2 #import "sjzOperation.h" 3 4 @interface ViewController () <sjzOperationDeleage> 5 6 @property (nonatomic, strong) UIImageView * imageView; 7 8 @end 9 10 @implementation ViewController 11 12 - (void)viewDidLoad { 13 [super viewDidLoad]; 14 15 UIImageView * imageView = [[UIImageView alloc] initWithFrame:self.view.bounds]; 16 self.imageView = imageView; 17 [self.view addSubview:imageView]; 18 19 sjzOperation * operatin = [[sjzOperation alloc] initWithURL:@"http://ww2.sinaimg.cn/crop.0.0.1080.1080.1024/d773ebfajw8eum57eobkwj20u00u075w.jpg"]; 20 operatin.deleage = self; 21 [operatin start]; 22 23 } 24 25 - (void)downloadFinish:(UIImage *)image 26 { 27 self.imageView.image = image; 28 } 29 30 @end
在mian方法中,添加了好多判断是否取消操作的代码。因为结束操作只有两种可能:1、操作顺利完成,2、取消操作。取消操作可能随时发生,甚至在一个操作开始执行之前,如果一个操作被取消,就不可能回收已经分配的资源。NSOperation对象需要定期地调用isCancelled方法检测操作是否已经被取消,如果返回YES(表示已取消),则立即退出执行。我们可以在下面情况下添加判断是否取消代码:
1、在执行任何实际工作之前
2、如果有循环,在每次循环中都要判断,如果代码比较长,那么判断要更频繁
3、在代码中的任何一个比较容易终止操作的点
并发操作
自定义操作,如果想实现并发操作,必须重写以下方法:
1、start方法(必须重写):所有并发操作必须重写此方法,并用自己的自定义实现替换默认行为。要手动执行操作,您调用它的start方法。因此,执行此方法是您的操作的起点,是您在其中设置线程或其他执行环境来执行任务的地方。
2、main方法(选择性重写):此方法通常用于执行与操作对象关联的任务。虽然可以在启动方法中执行任务,但使用该方法执行任务会导致你的设置和任务代码的更清洁分离
3、isExecuting、isFinished(必须重写):并发操作负责建立执行环境,并向外部客户报告该环境情况。因此,一个并发操作必须保持一些状态信息,以知道它合适执行它的任务和它何时完成任务。您的这些方法的实现必须是安全的与其他线程同时调用的时候。你也必须通过这些方法为关键路径创建适当KVO通知。
4、isConcurrent(必须重写):要确定一个操作为并发操作,重写此方法并返回YES。
sjzOperation.h
1 #import <Foundation/Foundation.h> 2 #import <UIKit/UIKit.h> 3 4 @protocol sjzOperationDeleage <NSObject> 5 6 - (void)downloadFinish:(UIImage *)image; 7 8 @end 9 10 @interface sjzOperation : NSOperation 11 { 12 BOOL executing; 13 BOOL finished; 14 } 15 16 - (void)completeOperation; 17 18 @property (nonatomic, strong) NSString * imageURL; 19 @property (nonatomic, weak) id<sjzOperationDeleage> deleage; 20 21 - (instancetype)initWithUrl:(NSString *)imageURL; 22 23 @end
sjzOperation.m
1 #import "sjzOperation.h" 2 3 @implementation sjzOperation 4 5 - (instancetype)initWithUrl:(NSString *)imageURL 6 { 7 self = [super init]; 8 if(self){ 9 self.imageURL = imageURL; 10 executing = NO; 11 finished = NO; 12 } 13 14 return self; 15 } 16 17 - (BOOL)isConcurrent 18 { 19 return YES; 20 } 21 22 //是否开始 23 - (BOOL)isExecuting 24 { 25 return executing; 26 } 27 28 //是否结束 29 - (BOOL)isFinished 30 { 31 return finished; 32 } 33 34 - (void)start 35 { 36 //在启动任务前要检查取消 37 if([self isCancelled]){ 38 //如果被取消,必须将操作移动到完成状态 39 [self willChangeValueForKey:@"isFinished"]; 40 finished = YES; 41 [self didChangeValueForKey:@"isFinished"]; return; 42 } 43 44 //如果不取消操作,开始执行任务 45 [self willChangeValueForKey:@"isExecuting"]; 46 [NSThread detachNewThreadSelector:@selector(main) toTarget:self withObject:nil]; 47 executing = YES; 48 [self didChangeValueForKey:@"isExecuting"]; 49 } 50 51 52 - (void)main 53 { 54 @autoreleasepool { 55 if(self.cancelled){ 56 return; 57 } 58 59 NSThread * thread = [NSThread currentThread]; 60 NSLog(@"%@", thread); 61 62 NSData * data = [NSData dataWithContentsOfURL:[NSURL URLWithString:self.imageURL]]; 63 64 if(self.isCancelled){ 65 return; 66 } 67 UIImage * image = [UIImage imageWithData:data]; 68 69 if(self.isCancelled){ 70 return; 71 } 72 73 if([self.deleage respondsToSelector:@selector(downloadFinish:)]){ 74 [self.deleage performSelector:@selector(downloadFinish:) withObject:image]; 75 } 76 77 [self completeOperation]; 78 } 79 } 80 81 - (void)completeOperation 82 { 83 [self willChangeValueForKey:@"isFinished"]; 84 [self willChangeValueForKey:@"isExecuting"]; 85 86 executing = NO; 87 finished = YES; 88 89 [self didChangeValueForKey:@"isExecuting"]; 90 [self didChangeValueForKey:@"isFinished"]; 91 } 92 93 @end
调用:
1 #import "ViewController.h" 2 #import "sjzOperation.h" 3 4 @interface ViewController () <sjzOperationDeleage> 5 6 @property (nonatomic, strong) UIImageView * imageView; 7 8 @end 9 10 @implementation ViewController 11 12 - (void)viewDidLoad { 13 [super viewDidLoad]; 14 15 self.imageView = [[UIImageView alloc] initWithFrame:self.view.bounds]; 16 [self.view addSubview:self.imageView]; 17 18 sjzOperation * operation = [[sjzOperation alloc] initWithUrl:@"http://ww2.sinaimg.cn/crop.0.0.1080.1080.1024/d773ebfajw8eum57eobkwj20u00u075w.jpg"]; 19 operation.deleage = self; 20 [operation start]; 21 } 22 23 - (void)downloadFinish:(UIImage *)image 24 { 25 self.imageView.image = image; 26 } 27 28 @end