多线程----NSOperation详解

简介
  • NSOpreation的作用
    p 配合NSOperation和NSOperationQueue也能够实现多线程编程

  • NSOperation 和 NSOperationQueue实现多线程的具体步骤
    p 先将需要执行的操作封装到一个NSOperation对象中
    p 然后将NSOperation对象添加到NSOperationQueue队列中
    p 系统会自动将NSOperationQueue中的NSOperation取出来
    p 将取出的NSOperation封装的操作放到一条新线程中执行

NSOpreation的子类
  • NSOpreation是个抽象类,并不具备封装操作的能力,

  • 使用NSOperation子类的方式有3种
    p NSInvocationOperation
    p NSBlockOperation
    p 自定义子类继承NSOperation,实现内部相应的方法。

NSInvocationOperation
  • 创建NSInvocationOperation对象
 NSInvocationOperation *p =[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
  • 调用start方法开始执行操作
  -(void)start
//一旦执行就会调用target的sel方法
  • 注意
    p 默认情况下,调用了start方法后并不会开启一条新线程去执行操作,而是在当前线程同步执行操作
    p 只有将NSOperation放到一个NSOperationQueue中,才会异步执行操作
     NSOperationQueue *queue = [[NSOperationQueue alloc] init];
     [queue addOperation:p];
NSBlockOperation
  • 创建NSBlockOperation对象
  NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
       
        //在主线程中执行
        NSLog(@"%@",[NSThread currentThread]);
        
    }];
  • 通过- (void)addExecutionBlock:(void (^)(void))block;方法添加更多的操作
    //添加额外的任务(在子线程执行)
      [op addExecutionBlock:^{
        NSLog(@"----%@",[NSThread currentThread]);
      }];
       [op start];
  • 注意: 只要NSBlockOperation封装的操作数大于1 ,就会异步执行操作
NSOperationQueue
  • NSOperationQueue的作用
    p NSOperation可以调用start方法来执行任务,但默认是同步执行的
    p 如果将NSOperation添加到NSOperationQueue(操作队列)中,系统会自动异步执行NSOperation中的操作
  • 添加操作到NSoperationQueue中
  - (void)addOperation:(NSOperation *)op;
  - (void)addOperationWithBlock:(void (^)(void))block;
  • (对比GCD)
  • GCD的队列类型
    • 并发队列
      • 自己创建的
      • 全局的
    • 串行队列
      • 主队列
      • 自己创建的
  • NSOperationQueue的队列类型
    • 主队列
    • [NSoperationQueue mainQueue]
    • 凡是添加到主队列中的任务,都会放到主线程中执行
    • 非主队列(其他队列)
      • [[NSOperationQueue alloc] init]
      • 同时包含了串行、并行功能
      • 添加到这种队列中的任务(NSOperation),就会被自动放到子线程中执行
  • 示例代码
  - (void)OperationQueue{
       //创建队列
        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
        //创建操作(任务)
    
        //创建NSInvocationOperation
         NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task1) object:nil];
    
      NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task2) object:nil];
    
      //创建NSBlockOperation
      NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        
          NSLog(@"task3 ---- %@",[NSThread currentThread]);
      }];
    
      [op3 addExecutionBlock:^{
        
          NSLog(@"task4 ---- %@",[NSThread currentThread]);
      }];
    
      [op3 addExecutionBlock:^{
        
          NSLog(@"task5 ---- %@",[NSThread currentThread]);
      }];
    
        [queue addOperation:op1];//内部会自动调 [op1 start];
        [queue addOperation:op2];//内部会自动调 [op2 start];
        [queue addOperation:op3];//内部会自动调 [op3 start];
        }
      - (void)task1{
          NSLog(@"task1----%@",[NSThread currentThread]);
      }

      - (void)task2{
          NSLog(@"task2----%@",[NSThread currentThread]);
      }
  • 自定NSOperation
    p 可以创建一个继承自NSOperation的类,在类的.m文件中重写方法- (void)main;

将需要执行的任务写在main方法中,然后 创建队列,实例化自定义类,将实例添加到队列中,也可以开启新线程执行任务。

p 示例代码

- (void)lgjOperation{

    //创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    //创建自定义NSOperationQueue
    LGJNSOperation *operation = [[LGJNSOperation alloc] init];
    
    [queue addOperation:operation];//[operation start];

}
//自定义Operation类
//.h文件
#import <Foundation/Foundation.h>

@interface LGJNSOperation : NSOperation

@end

//.m文件
#import "LGJNSOperation.h"

@implementation LGJNSOperation

//
-(void)main{

    NSLog(@"LGJNSOperation -- 下载图片---%@",[NSThread currentThread]);

}

@end
最大并发数
  • 什么是并发数
    p 同时执行的任务数
    p 比如同时开三个线程,执行三个任务,最大并发数就是3

  • 最大并发数的相关方法

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

p 示例代码

  //最大并发数
  - (void)OperationQueue1{
      
      //创建队列
      NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
      //设置最大并发数
      //queue.maxConcurrentOperationCount = 2;
      queue.maxConcurrentOperationCount = 1;//变成了串行队列
      
      [queue addOperationWithBlock:^{
        
          NSLog(@"task1---%@",[NSThread currentThread]);
        
      }];
      [queue addOperationWithBlock:^{
        
          NSLog(@"task2---%@",[NSThread currentThread]);
        
      }];
      [queue addOperationWithBlock:^{
        
          NSLog(@"task3---%@",[NSThread currentThread]);
        
      }];
      [queue addOperationWithBlock:^{
        
          NSLog(@"task4---%@",[NSThread currentThread]);
        
      }];

  }
队列的取消、暂停、恢复
  • 取消队列的所有操作
 - (void)cancelAllOperations;

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

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

-示例代码1

       - (void)viewDidLoad {
       [super viewDidLoad];

      [self OperationQueue];
      }

      -(void)OperationQueue{

        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
        queue.maxConcurrentOperationCount = 1;//设置为1就变成了串行队列
    
        //当执行了[self.queue cancelAllOperations];已经开启的线程是刹不住车的(意思是会继续打印),没有开启的线程将不会开启
        [queue addOperationWithBlock:^{
       
          for (NSInteger i = 0; i<1000 ; i++) {
            
              NSLog(@"task1 - %zd- %@",i,[NSThread currentThread]);
          }
      }];
    
      [queue addOperationWithBlock:^{
          
        for (NSInteger i = 0; i<1000 ; i++) {
            
            NSLog(@"task2 - %zd- %@",i,[NSThread currentThread]);
        }
      }];
    
      [queue addOperationWithBlock:^{
        
        for (NSInteger i = 0; i<1000 ; i++) {
            
            NSLog(@"task3 - %zd- %@",i,[NSThread currentThread]);
        }
      }];
    
      self.queue = queue;
    
      }

      -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{

        [self.queue cancelAllOperations];

    }
  • 示例代码2
      #import "ViewController.h"
      #import "LGJOperation.h"
      @interface ViewController ()
      @property(nonatomic,strong)NSOperationQueue *queue;

      @end

      @implementation ViewController
       - (void)viewDidLoad {
        [super viewDidLoad];
  
        [self OperationQueue];
      }

      -(void)OperationQueue{

        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
        queue.maxConcurrentOperationCount = 1;//设置为1就变成了串行队列
    
        //开启一条线程 执行自定义operation任务,任务的具体内容封装在自定义类的main方法中
        LGJOperation *op = [[LGJOperation alloc] init];
    
        [queue addOperation:op];
      
        self.queue = queue;
    
      }

      -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{

      [self.queue cancelAllOperations];

      }
      //自定义operation
      .h文件
      #import <Foundation/Foundation.h>

      @interface LGJOperation : NSOperation

      @end
      //.m文件
      #import "LGJOperation.h"
      @implementation LGJOperation

      -(void)main{
          for (NSInteger i = 0; i<5000 ; i++) {
             //也可以在这里终止
                if (i == 4000) {
            
                  if (self.cancelled)  return;
            }
        NSLog(@"task1 - %zd- %@",i,[NSThread currentThread]);
        }
      //如果想要终止线程中的任务,需要人为的做判断
        if (self.cancelled) return;
        
        for (NSInteger i = 0; i<1000 ; i++) {
        
            NSLog(@"task2 - %zd- %@",i,[NSThread currentThread]);
        }
        //可以在这里终止
        if (self.cancelled) return;
    
        for (NSInteger i = 0; i<1000 ; i++) {
        
            NSLog(@"task3 - %zd- %@",i,[NSThread currentThread]);
          }

        }

        @end
  • 示例代码2
      - (void)viewDidLoad {
        [super viewDidLoad];

          [self suspended];
   
        }
      //
       - (void)suspended{
    
        //创建队列
        self.queue1 =[[NSOperationQueue alloc] init];
    
        //设置最大并发数
        self.queue1.maxConcurrentOperationCount = 1;
    
        [self.queue1 addOperationWithBlock:^{
        
         [NSThread sleepForTimeInterval:2.0];
          NSLog(@"task1----%@",[NSThread currentThread]);
      }];
    
        [self.queue1 addOperationWithBlock:^{
        [NSThread sleepForTimeInterval:2.0];
        NSLog(@"task2----%@",[NSThread currentThread]);
      }];
      [self.queue1 addOperationWithBlock:^{
        [NSThread sleepForTimeInterval:2.0];
        NSLog(@"task3----%@",[NSThread currentThread]);
      }];
      }
      - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    
        if (self.queue1.isSuspended) {
          //恢复队列,继续执行
          self.queue1.suspended = NO;
        
      }else{
          // 暂停(挂起)队列,暂停执行
          self.queue1.suspended = YES;
        }
      }
操作依赖
  • NSOperation之间可以设置依赖来保证执行顺序
    p 比如一定要让操作A执行完以后,才能执行操作B,可以这么写

  • 示例代码

      - (void)viewDidLoad {
        [super viewDidLoad];

        [self Dependency];
   
      }

      //操作依赖
       - (void)Dependency{
    
      //创建队列
      NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
      NSInvocationOperation *operation1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run2:) object:@"1"];
    
      NSInvocationOperation *operation2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run2:) object:@"2"];
    
      NSInvocationOperation *operation3 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run2:) object:@"3"];
      //设置依赖
      [operation1 addDependency:operation2];//1 依赖于 2
      [operation2 addDependency:operation3];//2 依赖于 3      

      [queue addOperation:operation1];
      [queue addOperation:operation2];
      [queue addOperation:operation3];
     
    }

     - (void)run2:(NSString*)obj{
    
          NSLog(@"[current thread]==%@---obj===%@",[NSThread   currentThread],obj);
    
      }```

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

![屏幕快照 2016-06-17 上午10.48.51.png](http://upload-images.jianshu.io/upload_images/1970936-3be6eb8e07440d13.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

######操作的监听
- 可以监听一个操作的执行完毕
    ```-objc
        - (void (^)(void))completionBlock;
        - (void)setCompletionBlock:(void (^)(void))block; ```
 - 示例代码
  ```-objc
         NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
         NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
       
         NSLog(@"task1 === %@",[NSThread currentThread]);
       
         }];
   
         operation1.completionBlock = ^{
    
              NSLog(@"监听任务一执行完没1");
    
        };
    
      //    [operation1 setCompletionBlock:^{
      //        
      //        NSLog(@"监听任务一执行完没2");
      //    }];
    
        [queue addOperation:operation1];
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,542评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,596评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,021评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,682评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,792评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,985评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,107评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,845评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,299评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,612评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,747评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,441评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,072评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,828评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,069评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,545评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,658评论 2 350

推荐阅读更多精彩内容