美文网首页
NSOperation简单使用

NSOperation简单使用

作者: 3fbfd3c15df9 | 来源:发表于2017-11-07 23:29 被阅读5次

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

    NSOperation实现多线程的步骤

    1, 将要执行的操作封装到NSOperation对象中
    2, 将NSOperation 对象添加到 NSOperationQueue中
    3, 系统自动将NSOperationQueue中的NSOperation 取出来
    4, 将NSOperation 封装的操作放到一条新线程执行

    NSOperation的子类

    NSOperation是个抽象类,并不具有封装操作的能力。使用方式有3种
    1,NSInvocationOperation
    2,NSBlockOperation
    3,自定义子类继承NSOperation ,实现内部相应的方法

    NSOperationQueue 队列组

    1 ,默认就是并发执行的啦
    2, 那么我们怎么设置串行执行呢 。队列组有个属性 maxConcurrentOperationCount 设置最大并发数 ,就可以啦,这样就是串行执行的了
    maxConcurrentOperationCount = 1 ;串行队列
    maxConcurrentOperationCount > 1 ; 并发队列
    maxConcurrentOperationCount = 0 ; 不会执行任务
    maxConcurrentOperationCount = -1 ; 特殊意义。表示的是最大值。
    默认并发数就是 static const NSInteger NSOperationQueueDefaultMaxConcurrentOperationCount = -1;

    /*
      暂停可恢复,取消不可恢复
      队列中的任务也是有状态的:已经执行完毕 | 正在执行|排队等待 ,不管是暂停还是取消都会等待正在执行的任务结束才会执行。
    */
    
    //暂停  ,不能暂停当前正在执行的任务
        [queue setSuspended:YES];
    // 继续
         [queue setSuspended:NO];
    /*取消,不可恢复 。取消队列里面的所有任务,我们能拿到 cancelled 属性,
    这样就可以判断队列是否取消任务,可以在 队列任务里面进行判断,
    用于即时取消任务等,因为正在执行的任务不会里面执行,
    所有cancelled 。可以帮助判断*/
        [queue cancelAllOperations];
    
    NSOperationQueue 的一个只读属性 cancelled 。返回BOOL,队列是否取消了任务
    

    3,注意:串行执行 != 只开了一条线程

    方式一 :NSInvocationOperation 简单的封装任务,封装操作,不和NSOperationQueue配合使用的时候,没有任何作用,并不会开启子线程。

    
    -(void)invocationOperation{
        //1,封装操作,封装任务
        /*
         1.参数1:目标对象 self
         2.参数2:调用的方法
         3.参数3:方法的参数
         */
        NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test) object:nil];
        NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test1) object:nil];
        NSInvocationOperation *op3 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test2) object:nil];
    
        /*
         [NSOperationQueue mainQueue]; 获取主队列
         NSOperation队列:
         1,主队列 : [NSOperationQueue mainQueue] 和gcd主队列一样,任务都在主线程执行,主队列是串行队列
         2,非主队列:[[NSOperationQueue alloc] init] 特殊(同时具备并发和串行的功能),默认情况下非主队列是并发队列
         */
        
        //2 ,创建队列
       NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        //设置最大并发数的数量,同一时间最多多少任务可以执行 ,设置为1 就是串行啦
        queue.maxConcurrentOperationCount = 2;
        //3 , 添加操作到队列中 ,
        [queue addOperation:op1]; //内部已经调用了  [op1 start]; ,调用了start后又调用了内部的main方法 。我们可以用继承重写start,和main 方法。把任务封装到 main方法里面
        [queue addOperation:op2];
        [queue addOperation:op3];
        
        //,启动操作 ,如果不 和NSOperationQueue 配合操作。就必须手动启动一下,
        //并且NSInvocationOperation,单独使用并不会开启多线程
        //[op1 start];
    }
    
    -(void)test
    {
        NSLog(@"%s __func__  NSThread  = %@" ,__func__,[NSThread currentThread]);
    }
    -(void)test1
    {
        NSLog(@"%s __func__  NSThread  = %@" ,__func__,[NSThread currentThread]);
    }
    -(void)test2
    {
        NSLog(@"%s __func__  NSThread  = %@" ,__func__,[NSThread currentThread]);
    }
    /* 打印 ,看打印,不难理解,内部是并发执行
    2017-11-06 23:08:11.404 NSOperationDemo[1847:56183] -[ViewController test] __func__  NSThread  = <NSThread: 0x6080000776c0>{number = 4, name = (null)}
    2017-11-06 23:08:11.404 NSOperationDemo[1847:56165] -[ViewController test2] __func__  NSThread  = <NSThread: 0x60800007b140>{number = 5, name = (null)}
    2017-11-06 23:08:11.404 NSOperationDemo[1847:56166] -[ViewController test1] __func__  NSThread  = <NSThread: 0x60000007b9c0>{number = 3, name = (null)}
    */
    
    

    方式二 :NSBlockOperation 单独使用不配合 queue 队列, 不会开启子线程,但是一个操作中任务数量大于1 ,会开启子线程,详情如下

    -(void)blockOperation
    {
        //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]);
        }];
        
        
        //2 ,追加任务
        //注意: 如果一个操作中任务数量大于1 , 会开子线程并发执行任务,
        并不一定是子线程也许是主线程,比如op3 就有4个任务,所以会开子线程,
        并发执行任务。而 op1 ,op2,不会开子线程在主线程执行
        [op3 addExecutionBlock:^{
            NSLog(@"4 -- %@",[NSThread currentThread]);
        }];
        
        [op3 addExecutionBlock:^{
            NSLog(@"5 -- %@",[NSThread currentThread]);
        }];
       
        [op3 addExecutionBlock:^{
            NSLog(@"6 -- %@",[NSThread currentThread]);
        }];
        
        //3 ,启动
        [op1 start];
        [op2 start];
        [op3 start];
      
    }
    
    /* 打印结果
    2017-11-06 22:38:20.505 NSOperationDemo[1399:33218] 1 -- <NSThread: 0x60800006ba80>{number = 1, name = main}
    2017-11-06 22:38:20.505 NSOperationDemo[1399:33218] 2 -- <NSThread: 0x60800006ba80>{number = 1, name = main}
    2017-11-06 22:38:20.506 NSOperationDemo[1399:33218] 3 -- <NSThread: 0x60800006ba80>{number = 1, name = main}
    2017-11-06 22:38:20.506 NSOperationDemo[1399:33218] 5 -- <NSThread: 0x60800006ba80>{number = 1, name = main}
    2017-11-06 22:38:20.506 NSOperationDemo[1399:33259] 4 -- <NSThread: 0x600000076740>{number = 3, name = (null)}
    2017-11-06 22:38:20.506 NSOperationDemo[1399:33261] 6 -- <NSThread: 0x6000000766c0>{number = 4, name = (null)}
    */
    
    

    方式二:NSBlockOperation 单独使用配合 queue 队列,开启多线程

    #pragma mark NSBlockOperation 配合队列   queue 的 使用
    -(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]);
        }];
        
        //2 ,追加任务
        //注意: 如果一个操作中任务数量大于1 , 会开子线程并发执行任务,并不一定是子线程也许是主线程,比如op3 就有4个任务,所以会开子线程,并发执行任务
        [op3 addExecutionBlock:^{
            NSLog(@"4 -- %@",[NSThread currentThread]);
        }];
        
        [op3 addExecutionBlock:^{
            NSLog(@"5 -- %@",[NSThread currentThread]);
        }];
        
        [op3 addExecutionBlock:^{
            NSLog(@"6 -- %@",[NSThread currentThread]);
        }];
        
        //2 ,创建队列
        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        
        //3 ,启动
        [queue addOperation:op1];
        [queue addOperation:op2];
        [queue addOperation:op3];
        
        //简便方法 ,相当于,创建了任务,并添加到了 queue
        [queue addOperationWithBlock:^{
            NSLog(@"7 -- %@",[NSThread currentThread]);
        }];
    }
    
    /* 打印如下
      2017-11-06 23:17:08.596 NSOperationDemo[1886:61154] 7 -- <NSThread: 0x600000263940>{number = 7, name = (null)}
    2017-11-06 23:17:08.596 NSOperationDemo[1886:61099] 1 -- <NSThread: 0x60000007f140>{number = 6, name = (null)}
    2017-11-06 23:17:08.598 NSOperationDemo[1886:61155] 2 -- <NSThread: 0x6000002603c0>{number = 8, name = (null)}
    2017-11-06 23:17:08.598 NSOperationDemo[1886:61154] 3 -- <NSThread: 0x600000263940>{number = 7, name = (null)}
    2017-11-06 23:17:08.598 NSOperationDemo[1886:61113] 4 -- <NSThread: 0x60800007be40>{number = 4, name = (null)}
    2017-11-06 23:17:08.598 NSOperationDemo[1886:61099] 5 -- <NSThread: 0x60000007f140>{number = 6, name = (null)}
    2017-11-06 23:17:08.599 NSOperationDemo[1886:61155] 6 -- <NSThread: 0x6000002603c0>{number = 8, name = (null)
    */
    

    方式三:自定义子类继承NSOperation ,实现内部相应的方法

    好处:1,利于代码隐蔽
    2, 复用性,耦合性

    #import <Foundation/Foundation.h>
    //XCOperation 继承子类实现
    @interface XCOperation : NSOperation
    @end
    
    #import "XCOperation.h"
    @implementation XCOperation
    //告诉要执行的任务是什么
    -(void)main{
        NSLog(@"__Func__ %s , NSThread = %@",__func__,[NSThread currentThread]);
    }
    
    #pragma mark 继承 NSOperation  使用方法
    -(void)customOperation
    {
        //1 ,封装操作
        XCOperation *op1 = [[XCOperation alloc] init];
        XCOperation *op2 = [[XCOperation alloc] init];
    
        //2 ,创建队列
        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        
        //3 ,添加 启动
        [queue addOperation:op1];
        [queue addOperation:op2];
    }
    /* 打印
     NSOperationDemo[1950:69064] __Func__ -[XCOperation main] , NSThread = <NSThread: 0x600000074880>{number = 3, name = (null)}
     NSOperationDemo[1950:69044] __Func__ -[XCOperation main] , NSThread = <NSThread: 0x600000075680>{number = 4, name = (null)}
    */
    

    NSBlockOperation 操作依赖,控制任务顺序 ,监听任务完成情况。

    #pragma mark NSBlockOperation 操作依赖 ,控制任务顺序 ,于操作监听,监听某个任务完成状态
    -(void)blockOperationWithQueueRelyon
    {
        //1,创建任务
        NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"1 -- %@",[NSThread currentThread]);
        }];
        
        NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"2 -- %@",[NSThread currentThread]);
        }];
        
        NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
            for (int i = 0; i < 8; i ++) {
                NSLog(@"3 -- %@",[NSThread currentThread]);
                if (i == 7) {
                    NSLog(@"任务3 ,打印到了7 跳出循环任务结束");
                    break;
                }
            }
        }];
        
        NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"4 -- %@",[NSThread currentThread]);
        }];
        
        //2 ,创建队列
        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        NSOperationQueue *queue1 = [[NSOperationQueue alloc] init];
    
        //3.操作依赖
        //注意:不能循环依赖,比如操作1 依赖 操作3 。操作3 又来依赖操作1 。会互相等待
        //这里是操作1依赖操作3 。操作3 依赖操作2. 所有打印顺序是  2 -> 3 -> 1 -> 4
        //可以跨队列依赖
        [op1 addDependency:op3]; // 任务1 依赖任务3
        [op3 addDependency:op2]; // 任务3 依赖任务2
        [op4 addDependency:op1]; // 任务4 依赖任务1  所以 打印顺序是  2 -> 3 -> 1 -> 4
        
        //4.添加 到队列组
        [queue addOperation:op1];
        [queue addOperation:op2];
        [queue addOperation:op3];
        [queue1 addOperation:op4];
        
        //5,操作监听,监听任务3 ,任务3 完成了,打印这里
        op3.completionBlock = ^{
            NSLog(@"监听到任务3结束了 ,虽然监听到任务3结束,但是他和任务3并不一定在一个线程 ,%@",[NSThread currentThread]);
        };
        
    }
    /* 可以看出顺序是2 3 1 4 ,监听到了任务3
    2017-11-07 22:52:18.344 NSOperationDemo[1045:29786] 2 -- <NSThread: 0x60800007b7c0>{number = 7, name = (null)}
    2017-11-07 22:52:18.346 NSOperationDemo[1045:30254] 3 -- <NSThread: 0x608000077700>{number = 10, name = (null)}
    2017-11-07 22:52:18.346 NSOperationDemo[1045:30254] 3 -- <NSThread: 0x608000077700>{number = 10, name = (null)}
    2017-11-07 22:52:18.347 NSOperationDemo[1045:30254] 3 -- <NSThread: 0x608000077700>{number = 10, name = (null)}
    2017-11-07 22:52:18.347 NSOperationDemo[1045:30254] 3 -- <NSThread: 0x608000077700>{number = 10, name = (null)}
    2017-11-07 22:52:18.348 NSOperationDemo[1045:30254] 3 -- <NSThread: 0x608000077700>{number = 10, name = (null)}
    2017-11-07 22:52:18.348 NSOperationDemo[1045:30254] 3 -- <NSThread: 0x608000077700>{number = 10, name = (null)}
    2017-11-07 22:52:18.350 NSOperationDemo[1045:30254] 3 -- <NSThread: 0x608000077700>{number = 10, name = (null)}
    2017-11-07 22:52:18.350 NSOperationDemo[1045:30254] 3 -- <NSThread: 0x608000077700>{number = 10, name = (null)}
    2017-11-07 22:52:18.350 NSOperationDemo[1045:30254] 任务3 ,打印到了90 跳出循环任务结束
    2017-11-07 22:52:18.351 NSOperationDemo[1045:29786] 监听到任务3结束了 ,虽然监听到任务3结束,但是他和任务3并不一定在一个线程 ,<NSThread: 0x60800007b7c0>{number = 7, name = (null)}
    2017-11-07 22:52:18.351 NSOperationDemo[1045:30254] 1 -- <NSThread: 0x608000077700>{number = 10, name = (null)}
    2017-11-07 22:52:18.354 NSOperationDemo[1045:29786] 4 -- <NSThread: 0x60800007b7c0>{number = 7, name = (null)}
    
    */
    

    线程之间的通信 。例如: 子线程下载图片,下载完成通知主线程刷新

    #pragma makr 线程中的通信 ,例子子线程下载图片,主线程刷新
    
    -(void)downloadImage
    {
        //1 创建队列
        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
        //2 封装任务
        NSBlockOperation *download = [NSBlockOperation blockOperationWithBlock:^{
            NSURL *url = [NSURL URLWithString:@"http://static.firefoxchina.cn/img/201710/4_59e999c8e6aa50.jpg"];
            NSData *imageData = [NSData dataWithContentsOfURL:url];
            NSLog(@"子线程下载图片%@" ,[NSThread currentThread]);
            UIImage *image = [UIImage imageWithData:imageData];
          
            //4,线程通信,回到主线程,刷新UI
            [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                self.imageview.image = image ;
            }];
    
    //      [queue addOperationWithBlock:^{
    //           NSLog(@"7 -- %@",[NSThread currentThread]);
    //      }];
    //        [queue addOperationWithBlock:^{
    //            NSLog(@"8 -- %@",[NSThread currentThread]);
    //        }];
    //        [queue addOperationWithBlock:^{
    //            NSLog(@"9 -- %@",[NSThread currentThread]);
    //        }];
    
        }];
        
        //3 添加到queue
        [queue addOperation:download];
    }
    
    

    学习记录,简单的敲一下增强记忆,便于查找,😄

    NSThread简单的入门
    GCD入门

    相关文章

      网友评论

          本文标题:NSOperation简单使用

          本文链接:https://www.haomeiwen.com/subject/dyurmxtx.html