NSOperation简介

  • 配合使用NSOperation和NSOperationQueue也能实现多线程编程
    • NSOperation是个抽象类,并不具备封装操作的能力,必须使用它的子类
      • NSInvocationOperation
      • NSBlockOperation
      • 自定义子类继承NSOperation,实现内部相应的main方法
    • NSOperationQueue的作用
      • NSOperation可以调用start方法来执行任务,但默认是同步执行的
      • 如果将NSOperation添加到NSOperationQueue(操作队列)中,系统会自动异步执行NSOperation中的操作
        • 添加方法1:- (void)addOperation:(NSOperation *)op;
        • 添加方法2:- (void)addOperationWithBlock:(void (^)(void))block;
  • 使用NSInvocationOperation实现多线程编程
    • 步骤:
      • 封装操作 initWithTarget
      • 实现操作对应的方法 sel
      • 启动操作 start
    • 注意
      • 默认情况下,调用了start方法后并不会开一条新线程去执行操作,而是在当前线程同步执行操作
      • 只有将NSOperation放到一个NSOperationQueue中,才会异步执行操作
  • 使用NSBlockOperation实现多线程编程
    • 步骤:
      • 封装操作 blockOperationWithBlock
      • 追加操作 addExecutionBlock:(此步骤可以省略,即可以追加操作也可以不追加操作)
      • 启动操作 start
    • 注意
      • 只要NSBlockOperation封装的操作数>1,就会异步执行操作
      • 即只有追加操作,才会异步执行操作
  • 自定义子类继承NSOperation,实现内部相应的main方法
    • 创建自定义的子类继承NSOperation
    • 在子类中实现内部相应的main方法
      • 自己创建自动释放池(因为如果是异步操作,无法访问主线程的自动释放池)
      • 经常通过-(BOOL)isCancelled方法检测操作是否被取消,对取消做出响应
    • 实例化子类对象,并实现操作
  • 使用NSInvocationOperation和NSOperationQueue实现多线程编程
    • 步骤
      • 封装操作
      • 创建队列
        • 主队列mainQueue(凡是在主队列中的任务都在主线程中执行&串行队列)
        • 非主队列[[NSOperationQueue alloc]init](同时具备了并发和串行的功能,默认是并发队列)
      • 把操作添加到队列中
        • addOperation:
        • addOperationWithBlock:
  • 并发数
    • 概念:同时执行的任务数
    • 必须在操作添加到队列之前设置最大并发数的个数
    • 最大并发数的方法
        • (NSInteger)maxConcurrentOperationCount;
        • (void)setMaxConcurrentOperationCount:(NSInteger)cnt;
    • 最大并发数量
      • maxConcurrentOperationCount = 1 串行队列
      • maxConcurrentOperationCount > 1 并发队列
      • maxConcurrentOperationCount = -1 并发数不做限制,并发队列
      • maxConcurrentOperationCount = 0 程序不能运行
  • 同步执行任务的情况
    • 仅使用NSInvocationOperation
    • 仅使用NSBlockOperation
    • 使用NSOperation和mainQueue
    • 使用NSOperation和[[NSOperationQueue alloc]init]且最大并发数为0
  • 队列的暂停、恢复、取消
    • 队列的暂停
      • -(void)setSuspended:(BOOL)b; // YES代表暂停队列,NO代表恢复队列
      • -(BOOL)isSuspended;
      • 不能暂停当前处于执行状态的操作
      • 只能停止后面没有处于执行状态的操作
      • 暂停操作是可以恢复的
    • 队列的恢复
      • -(void)setSuspended:(BOOL)b; // YES代表暂停队列,NO代表恢复队列
      • -(BOOL)isSuspended;
    • 队列的取消
      • 取消所有队列的操作-(void)cancelAllOperations;,内部调用每个操作的cancel方法
      • 调用NSOperation的- (void)cancel方法取消单个操作
      • 后面的任务将永远没有机会执行
      • 取消操作是不能恢复的
    • 实现步骤
      • 在storyboard中拖拽开始执行、暂停、取消、恢复4个按钮
      • 生成其对应的点击事件的方法
      • 开始执行方法
        • 使用NSBlockOperation封装操作
        • 创建队列
        • 设置最大并发数为1(保证程序串行执行)
        • 把操作添加到队列
      • 暂停方法
        • 设置isSuspended属性为YES
      • 取消方法
        • 调用cancelAllOperations方法
      • 恢复方法
        • 设置isSuspended属性为NO
    • 实现代码
@interface ViewController ()

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

@end

@implementation ViewController
/**
 *  开始执行
 */
- (IBAction)startBtnClick {

    // 创建操作对象
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 10000; i++) {
            NSLog(@"1---%zd---main------%@", i,[NSThread currentThread]);
        }
    }];
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 10000; i++) {
            NSLog(@"2---%zd---main------%@", i,[NSThread currentThread]);
        }
    }];
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 10000; i++) {
            NSLog(@"3---%zd---main------%@", i,[NSThread currentThread]);
        }
    }];

    // 创建队列
    self.queue = [[NSOperationQueue alloc] init];

    // 设置最大并发数
    self.queue.maxConcurrentOperationCount = 1;

    // 把操作添加到队列
    [self.queue addOperation:op1];
    [self.queue addOperation:op2];
    [self.queue addOperation:op3];
}

/**
 *  暂停
 */
- (IBAction)suspendBtnClick {

    [self.queue setSuspended:YES];
}

/**
 *  取消
 */
- (IBAction)cancleBtnClick {

    [self.queue cancelAllOperations];
}

/**
 *  恢复
 */
- (IBAction)resumeBtnClick {

    [self.queue setSuspended:NO];
}

@end
  • 自定义NSOperatioin子类实现队列的取消
    • 自定义NSOperatioin子类
    • 重写main方法
    • 在storyboard中拖拽开始执行、取消2个按钮
      • 生成其对应的点击事件的方法
      • 开始执行方法
        • 使用自定义NSOperatioin子类封装操作
        • 创建队列
        • 设置最大并发数为1(保证程序串行执行)
        • 把操作添加到队列
      • 取消方法
        • 调用cancelAllOperations方法
    • 代码实现
// 控制器代码
@interface ViewController ()

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

@end

@implementation ViewController

- (IBAction)startBtnClick {

    // 封装操作
    ZQOperation *op = [[ZQOperation alloc] init];

    // 创建队列
    self.queue = [[NSOperationQueue alloc] init];

    // 设置最大并发数
    self.queue.maxConcurrentOperationCount = 1;

    // 把操作添加到队列
    [self.queue addOperation:op];

}

- (IBAction)cancelBtnClick {

    [self.queue cancelAllOperations];
}
@end
  • 操作的依赖、监听

    • 操作的依赖
      • NSOperation之间可以设置依赖来保证执行顺序
      • 可以在不同queue的NSOperation之间创建依赖关系
      • [operationB addDependency:operationA]; // 操作B依赖于操作A
      • 不能设置循环依赖:A依赖B,B依赖A
      • 设置依赖必须再把操作添加到队列之前
    • 操作的监听
      • -(void (^)(void))completionBlock;
      • -(void)setCompletionBlock:(void (^)(void))block;
      • 当要实现在A操作完成后实现B操作时需要监听A操作
  • NSOperation实现线程间的通信(下载两张图片并合成)

    • 思路
      • 封装下载图片1操作download1
      • 封装下载图片2操作download2
      • 封装合成图片操作combie(绘图)
      • 创建队列
      • 设置操作依赖
      • 把操作添加到队列中
    • 代码实现
    /** 下载两张图片并合成 */
    -(void)combieDownloadPicture{
    
    __block UIImage *image1;
    __block UIImage *image2;
    // 封装下载图片1操作
    NSBlockOperation *download1 = [NSBlockOperation blockOperationWithBlock:^{
        NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://pic14.nipic.com/20110522/7411759_164157418126_2.jpg"]];
        image1 = [UIImage imageWithData:data];
    }];
    
    // 封装下载图片2操作
    NSBlockOperation *download2 = [NSBlockOperation blockOperationWithBlock:^{
        NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://pic28.nipic.com/20130402/9252150_190139450381_2.jpg"]];
        image2 = [UIImage imageWithData:data];
    }];
    
    // 封装合成图片操作
    NSBlockOperation *combie = [NSBlockOperation blockOperationWithBlock:^{
        // 获取图形上下文
        UIGraphicsBeginImageContext(CGSizeMake(240, 240));
    
        // 画图1
        [image1 drawInRect:CGRectMake(0, 0, 240, 120)];
    
        // 画图2
        [image2 drawInRect:CGRectMake(0, 120, 240, 120)];
    
        // 生成图片
        UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    
        // 关闭图形上下文
        UIGraphicsEndImageContext();
    
        // 回到主线程刷新UI
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            self.imageView.image = image;
        }];
    }];
    
    // 创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    // 设置依赖
    [combie addDependency:download1];
    [combie addDependency:download2];
    
    // 把操作添加到队列中
    [queue addOperation:download1];
    [queue addOperation:download2];
    [queue addOperation:combie];
    

}

```

推荐阅读更多精彩内容