iOS多线程-NSOperation

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;

你可能感兴趣的:(iOS多线程-NSOperation)