多线程(三):NSOperation,NSOperationQueue,队列的最大并发数,队列的取消暂停和恢复,NSOperation操作依赖和监听,NSOperation线程间通信

NSOperation

  • NSOperation的作用

        配合使用NSOperation和NSOperationQueue也能实现多线程编程

  • NSOperation和NSOperationQueue实现多线程的具体步骤

       1)先将需要执行的操作封装到一个NSOperation对象中

       2)然后将NSOperation对象添加到NSOperationQueue中

       3)系统会自动将NSOperationQueue中的NSOperation取出来

       4)将取出的NSOperation封装的操作放到一条新线程中执行

  • NSOperation是个抽象类,并不具备封装操作的能力,必须使用它的子类, 使用NSOperation子类的方式有3种

       1)NSInvocationOperation

       2)NSBlockOperation

       3)自定义子类继承NSOperation,实现内部相应的方法


NSInvocationOperation

  • 创建NSInvocationOperation对象

       - (id)initWithTarget:(id)target selector:(SEL)sel object:(id)arg;

  • 调用start方法开始执行操作

        - (void)start;

        一旦执行操作,就会调用target的sel方法

  • 注意

       1)默认情况下,调用了start方法后并不会开一条新线程去执行操作,而是在当前线程同步执行操作

       2)只有将NSOperation放到一个NSOperationQueue中,才会异步执行操作


NSBlockOperation

  • 创建NSBlockOperation对象

       + (id)blockOperationWithBlock:(void (^)(void))block;

  • 通过addExecutionBlock:方法添加更多的操作

        - (void)addExecutionBlock:(void (^)(void))block;

注意:只要NSBlockOperation封装的操作数 > 1,就会异步执行操作


NSOperationQueue

  • NSOperationQueue的作用

       1)NSOperation可以调用start方法来执行任务,但默认是同步执行的

       2)如果将NSOperation添加到NSOperationQueue(操作队列)中,系统会自动异步执行NSOperation中的操作

  • 添加操作到NSOperationQueue中

       - (void)addOperation:(NSOperation *)op;

       - (void)addOperationWithBlock:(void (^)(void))block;


NSOperation和NSOperationQueue的功能代码

自定义的NSoperation:

  • 自定义NSOperation的步骤很简单:  重写- (void)main方法,在里面实现想执行的任务
  •  重写- (void)main方法的注意点

       1) 自己创建自动释放池(因为如果是异步操作,无法访问主线程的自动释放池)

       2) 经常通过- (BOOL)isCancelled方法检测操作是否被取消,对取消做出响应

AlanOperation.h

#import <Foundation/Foundation.h>

@interface AlanOperation : NSOperation

@end

AlanOperation.m

@implementation AlanOperation

//告知要执行的任务是什么
//1.有利于代码隐蔽
//2.复用性
-(void)main
{
    NSLog(@"main---%@",[NSThread currentThread]);
}
@end
#import "ViewController.h"
#import "AlanOperation.h"

@interface ViewController ()

@end

@implementation ViewController

#pragma mark ----------------------
#pragma mark Events
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    [self customWithQueue];
}

#pragma mark ----------------------
#pragma mark Methods

-(void)invocationOperationWithQueue
{
    //1.创建操作,封装任务
    /*
     第一个参数:目标对象 self
     第二个参数:调用方法的名称
     第三个参数:前面方法需要接受的参数 nil
     */
    NSInvocationOperation *op1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download1) object:nil];
    NSInvocationOperation *op2 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download2) object:nil];
    NSInvocationOperation *op3 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download3) object:nil];
    
    //2.创建队列
    /*
     GCD:
     串行类型:create & 主队列
     并发类型:create & 全局并发队列
     NSOperation:
     主队列:   [NSOperationQueue mainQueue] 和GCD中的主队列一样,串行队列
     非主队列: [[NSOperationQueue alloc]init]  非常特殊(同时具备并发和串行的功能)
     //默认情况下,非主队列是并发队列
     */
    NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    
    //3.添加操作到队列中
    [queue addOperation:op1];   //内部已经调用了[op1 start]
    [queue addOperation:op2];
    [queue addOperation:op3];
}

-(void)blockOperationWithQueue
{
    //1.创建操作
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"1----%@",[NSThread currentThread]);
       
    }];
    
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"2----%@",[NSThread currentThread]);
    }];
    
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"3----%@",[NSThread currentThread]);
    }];
    
    //追加任务
    [op2 addExecutionBlock:^{
        NSLog(@"4----%@",[NSThread currentThread]);
    }];
    
    [op2 addExecutionBlock:^{
        NSLog(@"5----%@",[NSThread currentThread]);
    }];
    
    [op2 addExecutionBlock:^{
        NSLog(@"6----%@",[NSThread currentThread]);
    }];
    
    //2.创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    
    //3.添加操作到队列
    [queue addOperation:op1];
    [queue addOperation:op2];
    [queue addOperation:op3];
    
    //简便方法
    //1)创建操作,2)添加操作到队列中
    [queue addOperationWithBlock:^{
        NSLog(@"7----%@",[NSThread currentThread]);
    }];
    
}

-(void)customWithQueue
{
    //1.封装操作
    AlanOperation *op1 = [[AlanOperation alloc]init];
    AlanOperation *op2 = [[AlanOperation alloc]init];
    
    //2.创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    
    //3.添加操作到队列
    [queue addOperation:op1];
    [queue addOperation:op2];
    
}


-(void)download1
{
    NSLog(@"%s----%@",__func__,[NSThread currentThread]);
}

-(void)download2
{
    NSLog(@"%s----%@",__func__,[NSThread currentThread]);
}


-(void)download3
{
    NSLog(@"%s----%@",__func__,[NSThread currentThread]);
}

@end

最大并发数

  • 什么是并发数

      1)同时执行的任务数

      2)比如,同时开3个线程执行3个任务,并发数就是3

      3)串行执行任务!=只开一条线程 (可以多条线程同步)
         maxConcurrentOperationCount >1 那么就是并发队列
         maxConcurrentOperationCount == 1 那就是串行队列
         maxConcurrentOperationCount == 0  不会执行任务
         maxConcurrentOperationCount == -1 特殊意义 最大值 表示不受限制

  • 最大并发数的相关方法

     1) - (NSInteger)maxConcurrentOperationCount;

     2) - (void)setMaxConcurrentOperationCount:(NSInteger)cnt;


队列的取消、暂停、恢复

  • 取消队列的所有操作

       - (void)cancelAllOperations;

      提示:也可以调用NSOperation的- (void)cancel方法取消单个操作

  • 暂停和恢复队列

     - (void)setSuspended:(BOOL)b; // YES代表暂停队列,NO代表恢复队列

     - (BOOL)isSuspended;


队列的并发、取消、暂停、恢复代码

#import "ViewController.h"


@interface ViewController ()
/** 队列 */
@property (nonatomic, strong) NSOperationQueue *queue;
@end

@implementation ViewController

#pragma mark ----------------------
#pragma mark Events

- (IBAction)startBtnClick:(id)sender
{
        //1.创建队列
    //默认是并发队列
    self.queue = [[NSOperationQueue alloc]init];
    
    //2.设置最大并发数量 maxConcurrentOperationCount
    //同一时间最多有多少个任务可以执行
    //串行执行任务!=只开一条线程 (线程同步)
    // maxConcurrentOperationCount >1 那么就是并发队列
    // maxConcurrentOperationCount == 1 那就是串行队列
    // maxConcurrentOperationCount == 0  不会执行任务
    // maxConcurrentOperationCount == -1 特殊意义 最大值 表示不受限制
    self.queue.maxConcurrentOperationCount = 1;
    
    //3.封装操作
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        for (NSInteger i = 0; i<1000; i++) {
            NSLog(@"1-%zd---%@",i,[NSThread currentThread]);
        }
    }];
    
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        for (NSInteger i = 0; i<1000; i++) {
            NSLog(@"2-%zd---%@",i,[NSThread currentThread]);
        }
    }];
    
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        for (NSInteger i = 0; i<1000; i++) {
            NSLog(@"3-%zd---%@",i,[NSThread currentThread]);
        }
    }];
    
    //4.添加到队列
    [self.queue addOperation:op1];
    [self.queue addOperation:op2];
    [self.queue addOperation:op3];
}

- (IBAction)suspendBtnClick:(id)sender
{
    //暂停,是可以恢复
    /*
     队列中的任务也是有状态的:已经执行完毕的 | 正在执行 | 排队等待状态
     */
    //不能暂停当前正在处于执行状态的任务
    [self.queue setSuspended:YES];
}

- (IBAction)goOnBtnClick:(id)sender
{
    //继续执行
    [self.queue setSuspended:NO];
}

- (IBAction)cancelBtnClick:(id)sender
{
    //取消,不可以恢复
    //该方法内部调用了所有操作的cancel方法
    [self.queue cancelAllOperations];
}

@end

NSOperation操作依赖和监听

  • NSOperation之间可以设置依赖来保证执行顺序

       比如一定要让操作A执行完后,才能执行操作B,可以这么写

       [operationB addDependency:operationA]; // 操作B依赖于操作A

  • 可以在不同queue的NSOperation之间创建依赖关系

注意:不能相互依赖(A依赖B,B依赖A),也不能循环依赖

  • 可以监听一个操作的执行完毕

       - (void (^)(void))completionBlock;

       - (void)setCompletionBlock:(void (^)(void))block;

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    //1.创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    NSOperationQueue *queue2 = [[NSOperationQueue alloc]init];
    
    //2.封装操作
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"1---%@",[NSThread currentThread]);
    }];
    
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"2---%@",[NSThread currentThread]);
    }];
    
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"3---%@",[NSThread currentThread]);
    }];
    
    NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"4---%@",[NSThread currentThread]);
    }];
    
    //操作监听
    op3.completionBlock = ^{
        NSLog(@"++++客官,来看我吧------%@",[NSThread currentThread]);
    };
    
    //添加操作依赖
    //注意点:不能循环依赖
    //可以跨队列依赖
    [op1 addDependency:op4];
//    [op4 addDependency:op1];
    
    [op2 addDependency:op3];
    
    //添加操作到队列
    [queue addOperation:op1];
    [queue addOperation:op2];
    [queue addOperation:op3];
    [queue2 addOperation:op4];
    
}

@end

NSOperation线程间通信

#import "ViewController.h"

@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@end

@implementation ViewController

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    [self comBie];
}

-(void)download
{
    
    //http://s15.sinaimg.cn/bmiddle/4c0b78455061c1b7f1d0e
    
    //1.开子线程下载图片
    //1.1 非主队列
    NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    
    //1.2 封装操作
    NSBlockOperation *download = [NSBlockOperation blockOperationWithBlock:^{
        
        NSURL *url = [NSURL URLWithString:@"http://s15.sinaimg.cn/bmiddle/4c0b78455061c1b7f1d0e"];
        NSData *imageData = [NSData dataWithContentsOfURL:url];
        UIImage *image = [UIImage imageWithData:imageData];
        
        NSLog(@"download---%@",[NSThread currentThread]);
        
        //3.更新UI
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            self.imageView.image = image;
            NSLog(@"UI---%@",[NSThread currentThread]);
        }];
        
    }];
    
    //2.添加操作到队列
    [queue addOperation:download];
}

/*
 1.下载图片1
 2.下载图片2
 3.合并图片
 */
-(void)comBie
{
    //1.创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    
    __block UIImage *image1;
    __block UIImage *image2;
    //2 封装操作,下载图片1
    NSBlockOperation *download1 = [NSBlockOperation blockOperationWithBlock:^{
        
        NSURL *url = [NSURL URLWithString:@"http://s15.sinaimg.cn/bmiddle/4c0b78455061c1b7f1d0e"];
        NSData *imageData = [NSData dataWithContentsOfURL:url];
        image1 = [UIImage imageWithData:imageData];
        
        NSLog(@"download---%@",[NSThread currentThread]);
        
    }];
    
    //3 封装操作,下载图片2
    NSBlockOperation *download2 = [NSBlockOperation blockOperationWithBlock:^{
        
        NSURL *url = [NSURL URLWithString:@"http://www.027art.com/feizhuliu/UploadFiles_6650/201109/2011091718442835.jpg"];
        NSData *imageData = [NSData dataWithContentsOfURL:url];
        image2 = [UIImage imageWithData:imageData];
        
        NSLog(@"download---%@",[NSThread currentThread]);
        
    }];
    
    //4.封装合并图片的操作
    NSBlockOperation *combie = [NSBlockOperation blockOperationWithBlock:^{
        //4.1 开上下文
        UIGraphicsBeginImageContext(CGSizeMake(200, 200));
        
        //4.2 画图1
        [image1 drawInRect:CGRectMake(0, 0, 100, 200)];
        
        //4.3 画图2
        [image2 drawInRect:CGRectMake(100, 0, 100, 200)];
        
        //4.4 根据上下文得到图片
        UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
        
        //4.5 关闭上下文
        UIGraphicsEndImageContext();
        
        //7.更新UI
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            self.imageView.image = image;
            NSLog(@"UI----%@",[NSThread currentThread]);
        }];
        
    }];
    
    //5.设置依赖关系
    [combie addDependency:download1];
    [combie addDependency:download2];
    
    //6.添加操作到队列中
    [queue addOperation:download2];
    [queue addOperation:download1];
    [queue addOperation:combie];
}
@end

猜你喜欢

转载自blog.csdn.net/weixin_42433480/article/details/89644920
今日推荐