往线程池(NSOperationQueue)中添加任务(NSOperation),线程池中的线程可看作消费者,从队列中取走任务,并执行它。
一、NSOperationQueue多线程的优缺点:
- 优点:不需要关心线程管理,数据同步的事情,可以把精力放在自己需要执行的操作上;
Cocoa operation相关的类是NSOperation,NSOperationQueue。NSOperation是个抽象类,使用它必须用它的子类,可以实现它或者使用它定义好的两个子类:NSInvocationOperation和NSBlockOperation。创建NSOperation子类的对象,把对象添加到NSOperationQueue队列里执行;
二、NSOperation简介
- ①、一个NSOperation对象可以通过调用start方法来执行任务,默认是同步的;
- ②、将NSOperation添加到一个NSOperationQueue(操作队列)中去执行,而且异步执行的。
创建一个操作队列:
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
三、使用NSOperation的两种方法
1、使用定义好的两个类:NSInvocationOperation和NSBlockOperation
例如:
#define kURL @"http://avatar.youkuaiyun.com/2/C/D/1_totogo2010.jpg"
- ①、创建线程池,并且添加任务
NSInvocationOperation *operation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(downloadImage:) object:kURL];
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
[queue addOperation:operation];
- ②、执行线程任务,完成后返回主线程显示
-(void)downloadImage:(NSString *)url{
NSURL *nsUrl = [NSURL URLWithString:url];
NSData *data = [[NSData alloc]initWithContentsOfURL:nsUrl];
UIImage * image = [[UIImage alloc]initWithData:data];
[self performSelectorOnMainThread:@selector(updateUI:) withObject:image waitUntilDone:YES];
}
- ③、在主线程中显示如片:
-(void)updateUI:(UIImage*) image{
self.imageView.image = image;
}
2、继承NSOperation
在.m文件中实现main方法,main方法编写执行的代码即可;
例如:
- ①、自定义类MYOperation继承NSOperation
#import <Foundation/Foundation.h>
@interface MyOperation : NSOperation
@property (nonatomic, assign) int time;
@end
- ②、实现main方法,执行任务
#import "MyOperation.h"
@implementation MyOperation
- (void)main{
sleep(self.time);
NSLog(@"%d",self.time);
}
@end
- ③、在页面中创建操作队列,添加任务
- (void)operation{
//操作队列
NSOperationQueue* queue = [[NSOperationQueue alloc] init];
//最大并行数量
queue.maxConcurrentOperationCount = 10;
for (int i = 1; i < 11; i++) {
//操作
MyOperation* oper = [[MyOperation alloc] init];
oper.time = i;
//添加到队列中
[queue addOperation:oper];
[oper release];
}
}
四、添加NSOperation到NSOperationQueue
1、添加一个NSOperation
- (void)addOperation:(NSOperation *)op;
例如:
[queue addOperation:operation];
2、添加一组NSOperation
- (void)addOperations:(NSArray *)ops waitUntilFinished:(BOOL)wait;
例如:
[queue addOperations:operations waitUntilFinished:NO];
3、添加一个Block形式的NSOperation
- (void)addOperationWithBlock:(void (^)(void))block;
例如:
[queue addOperationWithBlock:^{
NSLog(@"%d",i);
}];
NSOperation添加到queue之后,通常短时间内就会得到运行。但是如果存在依赖,或者整个queue被暂停等原因,也金额能需要等待。
注意:NSOperation添加到queue之后,绝对不要在修改NSOperation对象的状态。因为NSOperation对象可能会在任何时候运行,因此改变NSOperation对象的依赖或数据会产生不利的影响。你只能查看NSOperation对象的状态,比如是否正在运行、等待运行、已经完成等。
五、添加NSOperation的依赖对象
1、NSOperation依赖
当某个NSOperation对象依赖于其他NSOperation对象的完成时,就可以通过addDependency党法添加一个或者多个依赖的对象,只有所有依赖的对象都已经完成操作,当前NSOperation对象才会开始执行操作。另外,通过removeDependency方法来删除依赖对象。
- (void)addDependency:(NSOperation *)op;
例如:
[oper addDependency:oper0];
- (void)removeDependency:(NSOperation *)op;
例如:
[oper removeDependency:oper0];
依赖关系不局限于相同queue中的NSOperation对象,NSOperation对象会管理自己的依赖,因此完全可以在不同的queue之间的NSOperation对象创建依赖关系。
唯一的限制是不能环形依赖。
2、依赖关系会影响到NSOperation对象在queue中的执行顺序
六、修改Operation的执行顺序
对于添加到queue中的operation,它们的执行顺序确定于两点:
- ①、首先看看NSOperation是否已经准备好:是否准备好由对象的依赖关系确定;
- ②、然后在根据所有NSOperation的相关优先级确定。优先级等级则是operation对象本身的一个属性。默认所有operation都拥有“普通”优先级,不过可以通过setQueuePriority:方法提升或降低operation对象的优先级。优先级只能应用于相同queue中的operations。如果应用有多个operation queue,每个queue的优先等级是相互独立的。因此不同queue中的低优先级操作任然可能比高优先级操作更早执行。
注意:优先级不能替代依赖关系,优先级只是对已经准备好的operation确定执行顺序。先满足依赖关系,然后再根据优先级从所有准备好的操作中选择优先级最高的那个执行。
例如:
[oper setQueuePriority:NSOperationQueuePriorityNormal];
typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
NSOperationQueuePriorityVeryLow = -8L,
NSOperationQueuePriorityLow = -4L,
NSOperationQueuePriorityNormal = 0,
NSOperationQueuePriorityHigh = 4,
NSOperationQueuePriorityVeryHigh = 8
};
七、设置队列的最大并发操作数量
队列的最大并发操作数量,意思时队列中最多同时运行几条线程
虽然NSOperationQueue类设计用于并发执行operations,你也可以强制单个queue一次只能执行一个operation。默认为-1,setMaxConcurrentOperationCount:方法可以配置queue的最大并发操作数量。设为1就表示queue每次只能执行一个操作。不过operation执行的顺序仍然依赖于其它因数,比如operation是否准备好和operation的优先级等。因此串行化的operation queue并不等同于GCD中的串行dispatch queue。
例如:
[queue setMaxConcurrentOperationCount:1];
八、取消operation
一旦添加到operation queue,queue就拥有了这个Operation对象并且不能被删除,唯一能做的事情是取消。你可以调用Operation对象cancel方法取消单个操作,也可以调用operation queue的cancelAllOperations方法取消当前queue中的所有操作。
例如:
//取消单个操作
[oper cancel];
//取消所有操作
[queue cancelAllOperations];
九、等待Options完成
为了最佳的性能,你应该设计你的应用尽可能地一步操作,让应用在Operation正在执行时可以去处理其它事情。如果需要在当前线程中处理operation完成后的结果,可以使用NSOperation的waitUntilFinished方法阻塞当前线程,等待operation完成。通常那个我们应该避免编写这样的代码,阻塞当前线程可能是一种简便的解决方案,但是它引入了更多的串行代码,限制了整个应用的并发性,同时也降低了用户体验。绝对不要在应用主线程中等待一个Operation,只能在第二或次要线程中等待。阻塞主线程将导致应用无法响应用户事件,应用也将表现为无响应。
例如:
// 会阻塞当前线程,等到某个operation执行完毕
[oper waitUntilFinished];
除了等待单个Operation完成,你也可以同时等待一个queue中的所有操作,使用NSOperationQueue的waitUntilAllOperationAreFinished方法。注意:在等待一个queue时,应用的其他线程仍然可以往queue中添加Operation,因此可能会加长线程的等待时间。
例如:
// 阻塞当前线程,等待queue的所有操作执行完毕
[queue waitUntilAllOperationsAreFinished];
十、暂停或继续queue
如果你想暂时暂停Operation的执行,可以使用queue的setSuspended:方法暂停queue。不过暂停一个queue不会导致正在执行的operation在任务中途暂停,只是简单地阻止调度新Operation执行。你可以在响应用户请求时,暂停一个queue来暂停等待中的任务。稍后根据用户的请求,可以再次调用setSuspended:方法继续queue中operation的执行。
例如:
// 暂停queue
[queue setSuspended:YES];
// 继续queue
[queue setSuspended:NO];