1 NSOperation
他是一个抽象类,能够与NSOperationQueue结合实现多线程操作。要使用NSOperation,需要实现它的子类:NSInvocationOperation、NSBlockOperation和自定义类。
实现思路
(1)创建NSOperationQueue队列对象
(2)执行操作
①start开始:一律在主线程中同步执行
②加入队列,自动异步并发执行
1.2 NSInvocationOperation
-
如果直接使用start方法,那么操作会在主线程中同步执行,不会创建新县城
-
如果加入队列,操作会异步并发执行,会开辟新线程
-(void)viewDidLoad { //1 实例化队列 NSOperationQueue *queue = [[NSOperationQueue alloc]init]; //2 创建操作 NSInvocationOperation *operation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download) object:nil]; //3.1手动开始 //[operation start]; //3.2加入队列自动开始 [queue addOperation:operation]; } //下载任务 -(void)download { NSLog(@"正在下载图片---%@",[NSThread currentThread]); }
1.3 NSBlockOperation
实现思路:①创建队列 ②创建操作 ③加入队列
任务封装在block中。自动并发执行
- (void)viewDidLoad {
[super viewDidLoad];
//1 创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//2.1 创建操作1
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"任务1-正在下载图片1----%@",[NSThread currentThread]);
}];
//2.2 创建操作2
NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"任务1-正在下载图片2----%@",[NSThread currentThread]);
}];
//2.3 创建操作3
NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"任务1-正在下载图片3----%@",[NSThread currentThread]);
}];
//3.1 加入队列方式1
[queue addOperation:operation1];
//3.2 加入队列方式2
[queue addOperations:@[operation2,operation3] waitUntilFinished:YES];
}
也可以同一个操作中添加多个任务,每个任务也可以异步并发执行。使用addExecuteionBlock:方法来添加任务。
- (void)viewDidLoad {
[super viewDidLoad];
//1 创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//2 创建操作1
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"任务1-正在下载图片1----%@",[NSThread currentThread]);
}];
//3 继续添加任务到操作
[operation1 addExecutionBlock:^{
NSLog(@"任务2-正在下载图片2----%@",[NSThread currentThread]);
}];
//4 将操作加入队列
[queue addOperation:operation1];
}
1.4 NSOperationQueue队列
获取主队列
-
通过mainQueue方法来获取程序的主队列,通常用来做线程之间的通信
-
加入主队列的操作,都是在主线程中执行的。
//获取当前的主队列 NSOperationQueue *queue = [NSOperationQueue mainQueue];
手动创建队列
手动创建的队列,默认是并发队列,是否开启新线程,需要根据maxConcurrentOperationCount(最大并发线程数)来决定
//手动创建队列
NSOperationQueue *queue = [NSOperationQueue alloc]init];
1.5 线程之间的通信
线程之间的通信,一般用在这样的场景:耗时操作,比如下载操作会放在子线程中处理,当子线程结束后,需要回到主线程中设置刷新UI。一般使用主队列来回到主线程。
- (void)viewDidLoad {
[super viewDidLoad];
//1 创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//2 创建操作
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
//2.1 执行耗时操作
NSURL *url = [NSURL URLWithString:@"http://image1.png"];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
NSLog(@"图片下载完毕-----%@",[NSThread currentThread]);
//2.2 获取主队列,回到主线程执行block里面的任务
[[NSOperationQueue mainQueue]addOperationWithBlock:^{
//在主线程中,刷新UI
self.imageView.image = image;
}];
}];
//3 加入队列
[queue addOperation:operation];
}
1.6 线程间的依赖
任务A、B、C异步处理,但是任务B的完成依赖于任务A的完成,任务C的完成依赖于任务B的完成。这就叫做线程间的依赖。可以通过[operation addDependency:Oper1]来添加依赖。
eg、分别下载图片1和logo图片,然后在新线程中合并两张图片(打上水印)。合并操作依赖于两个下载线程,这样就使用到了线程间的依赖。
__block UIImage *image1 = nil;
__block UIImage *logo = nil;
//1 创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//2 创建操作A
NSBlockOperation *operationA = [NSBlockOperation blockOperationWithBlock:^{
//下载图片1
NSURL *url = [NSURL URLWithString:@"http://img3.duitang.com/uploads/item/201408/22/20140822234055_W3CrG.thumb.700_0.jpeg"];
NSData *data = [NSData dataWithContentsOfURL:url];
image1 = [UIImage imageWithData:data];
NSLog(@"图片下载完成1--%@",[NSThread currentThread]);
}];
//3 创建操作B
NSBlockOperation *operationB = [NSBlockOperation blockOperationWithBlock:^{
//下载logo
NSURL *url = [NSURL URLWithString:@"http://img0.bdstatic.com/img/image/imglogo-r.png"];
NSData *data = [NSData dataWithContentsOfURL:url];
logo = [UIImage imageWithData:data];
NSLog(@"logo下载完成--%@",[NSThread currentThread]);
}];
//4 创建操作C
NSBlockOperation *operationC = [NSBlockOperation blockOperationWithBlock:^{
//合成图片
UIGraphicsBeginImageContextWithOptions(image1.size, NO, 0.0);
[image1 drawInRect:CGRectMake(0, 0, image1.size.width, image1.size.height)];
[logo drawInRect:CGRectMake(0, 0, 100, 50)];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
NSLog(@"操作C--%@",[NSThread currentThread]);
//获取主队列,回到主线程刷新UI
[[NSOperationQueue mainQueue]addOperationWithBlock:^{
self.imageView.image = image;
NSLog(@"合成图片--%@",[NSThread currentThread]);
}];
}];
//4 创建依赖
[operationC addDependency:operationA];
[operationC addDependency:operationB];
//5 加入队列
[queue addOperations:@[operationA,operationB,operationC] waitUntilFinished:NO];
1.7 其他高级用法
取消队列任务
使用场景:通常使用在didReceiveMemoryWarning方法中,当收到内存告警后,取消队列中所有的任务。
//取消队列中所有的任务
[queue cancelAllOperations];
暂停队列任务
使用场景:
-
当正在下载时,如果碰到用户滚动界面UI时,暂停任务,为了用户体验。
-
在用户停止滚动时,恢复任务。
//暂停队列中的任务
[queue setSuspended:YES];
//恢复队列中的任务
[queue setSuspended:NO];
设置最大并发线程数
//设置最大并发线程数
queue.maxConcurrentOperationCount = 2;
网友评论