美文网首页
OC高级-NSOperation 和 NSOperationQu

OC高级-NSOperation 和 NSOperationQu

作者: yanhooIT | 来源:发表于2016-08-25 00:15 被阅读273次

    实现多线程的具体步骤

    • 先将需要执行的操作封装到一个NSOperation对象
    • 然后将NSOperation对象添加到NSOperationQueue
    • 系统自动从NSOperationQueue中取出NSOperation对象放到适合的线程中执行

    NSOperation

    • NSOperation是个抽象类,并不具备封装操作的能力
    • 只能通过它的子类来使用它的功能
    • 有三种方式来使用NSOperation
      • NSInvocationOperation
      • NSBlockOperation
      • 自定义继承自NSOperation的操作

    NSInvocationOperation

    • 创建NSInvocationOperation对象,通过对象方法创建
    - (id)initWithTarget:(id)target selector:(SEL)sel object:(id)arg;
    
    • 必须手动调用start方法才开始执行操作
    • 默认情况下,调用了start方法后并不会开一条新线程去执行操作,而是在当前线程中同步执行操作
    - (void)invocationOperation
    {
        NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
    
        // 调用start方法
        [op start];
    }
    
    - (void)run
    {
        NSLog(@"------%@", [NSThread currentThread]);
    }
    

    NSBlockOperation

    • 创建NSBlockOperation对象,通过类方法创建
    + (id)blockOperationWithBlock:(void (^)(void))block;
    
    • 通过addExecutionBlock:方法添加多个操作
    - (void)addExecutionBlock:(void (^)(void))block;
    
    • 必须手动调用start方法才开始执行操作
    - (void)blockOperation
    {
        NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^
        {
            // 这个任务是在【主线程】
            NSLog(@"下载1------%@", [NSThread currentThread]);
        }];
    
        // 添加额外的任务(在子线程执行)
        [op addExecutionBlock:^{
            NSLog(@"下载2------%@", [NSThread currentThread]);
        }];
        [op addExecutionBlock:^{
            NSLog(@"下载3------%@", [NSThread currentThread]);
        }];
        [op addExecutionBlock:^{
            NSLog(@"下载4------%@", [NSThread currentThread]);
        }];
    
        [op start];
    }
    
    • 第一个任务是在当前线程中执行,后续的任务才会在子线程中执行
    • 只有当封装的操作数> 1,才会异步执行操作

    自定义继承自NSOperation的操作

    • 重写- (void)main方法,在里面实现想执行的任务逻辑
    • 自己创建自动释放池,如果操作是在子线程中完成,那么子线程中是无法访问主线程的自动释放池,所以自定Operation需要在外层添加@autoreleasepool{ }
    • 合理布局,在适当位置调用isCancelled属性检测操作是否已被取消,及时中断操作
    - (void)main
    {
        @autoreleasepool
        {
            for (NSInteger i = 0; i < 1000; i++)
            {
                NSLog(@"download1 -%zd-- %@", i, [NSThread currentThread]);
            }
    
            // 由于任务一旦开始执行就没办法停止下来
            // 苹果官方建议,如果是自定义的Operation,而且内部的执行逻辑很耗时
            // 如果外面调用了cancel方法,可以通过在一段耗时逻辑后调用一次isCancelled方法判断操作是否已经取消,以用来中断任务的执行
            
            // 合理布局此方法
            if (self.isCancelled) return;
    
            for (NSInteger i = 0; i< 1000; i++)
            {
                NSLog(@"download2 -%zd-- %@", i, [NSThread currentThread]);
            }
    
            if (self.isCancelled) return;
    
            for (NSInteger i = 0; i < 1000; i++)
            {
                NSLog(@"download3 -%zd-- %@", i, [NSThread currentThread]);
            }
    
            if (self.isCancelled) return;
        }
    }
    

    NSOperation设置依赖

    • 比如任务A执行完后,才能执行任务B,可以这么设置
    // 操作B依赖于操作A
    [operationB addDependency:operationA];
    
    • 可以在不同队列的NSOperation之间创建依赖关系


      不同队列的NSOperation之间创建依赖关系
    • NSOperation之间不能相互依赖,比如:A依赖B,B依赖A
    - (void)addDependencyTest
    {
        // 直接创建
        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
        NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"download1----%@", [NSThread  currentThread]);
        }];
        NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"download2----%@", [NSThread  currentThread]);
        }];
        NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"download3----%@", [NSThread  currentThread]);
        }];
    
        // 设置依赖后再添加到队列
        // 当op1和op2都执行完才执行op3,但是op1和op2谁先执行完不确定
        [op3 addDependency:op1];
        [op3 addDependency:op2];
    
        // 【不能】循环依赖
        //    [op3 addDependency:op1];
        //    [op1 addDependency:op3];
    
        [queue addOperation:op1];
        [queue addOperation:op2];
        [queue addOperation:op3];
    }
    
    • NSOperation执行完毕后想做一些收尾的事情,可以设置completionBlock属性
    NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^
    {
        NSLog(@"download----%@", [NSThread  currentThread]);
    }];
    
    // 任务执行完毕就会执行这个block
    op.completionBlock = ^
    {
        // 也是在子线程中执行
        NSLog(@"op执行完毕后执行---%@", [NSThread currentThread]);
    };
    

    NSOperationQueue

    • 直接创建NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    • 如果将NSOperation添加到NSOperationQueue中,系统会自动异步执行NSOperation中的操作
    • 本质上NSOperationQueue内部也是自动调用了NSOperation的start方法来执行任务
    • 同时包含了串行 和 并行功能

    添加操作到NSOperationQueue中方法

    - (void)addOperation:(NSOperation *)op;
    
    // 推荐使用此种方式添加任务
    - (void)addOperationWithBlock:(void (^)(void))block;
    

    最大并发数

    • 同时执行的最大任务数
    • 当最大并发数为1的时候就是串行队列,但所有任务不一定同一个线程上执行完,可能在多个线程上执行,但一定是按顺序执行
    • 最大并发数设置方式
    // 调用示例
    queue.maxConcurrentOperationCount = 1;
    

    暂停和恢复队列:suspended

    • 暂停后还未执行的任务将不会被执行,但是已经开始的任务是不能立即停下来了
    // 调用示例
    queue.suspended = !self.queue.suspended;
    
    • suspended的应用场景:队列正在执行一些耗时操作,当用户执行滚动操作时,为了用户体验可以调用这个属性先让队列中的任务暂停执行,以用来提高用户体验

    取消队列的所有操作:cancelAllOperations

    • 取消队列中的所有任务,会将队列中的任务全部移除
    • 这个方法内部其实是调用了NSOperation中的cancel方法
    • 所以可以通过NSOperation中的cancel方法来取消单个操作
    • 只能取消还未执行的任务,已经开始的任务不能取消,除非任务中主动判断了是否取消操作queue.isCancelled来中断操作
    // 调用示例
    [queue cancelAllOperations];
    

    线程间通信

    - (void)test
    {
        [[[NSOperationQueue alloc] init] addOperationWithBlock:^
        {
            // 图片的网络路径
            NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
    
            // 加载图片
            NSData *data = [NSData dataWithContentsOfURL:url];
    
            // 生成图片
            UIImage *image = [UIImage imageWithData:data];
    
            // 回到主线程
            [[NSOperationQueue mainQueue] addOperationWithBlock:^
            {
                self.imageView.image = image;
            }];
        }];
    }
    

    GCD的队列和NSOperationQueue队列对比

    GCD的队列

    • 并发队列
      • 自己创建的(DISPATCH_QUEUE_CONCURRENT
      • 全局队列
    • 串行队列
      • 自己创建的(DISPATCH_QUEUE_SERIAL or NULL
      • 主队列(特殊的串行队列)

    NSOperationQueue队列类型

    • 主队列
      • [NSOperationQueue mainQueue]
      • 凡是添加到主队列中的任务(NSOperation),都会放到主线程中执行
    • 非主队列(其他队列)
      • 创建:[[NSOperationQueue alloc] init]
      • 同时包含了串行 和 并行功能
      • 当设置最大并发数为queue.maxConcurrentOperationCount = 1时就是串行队列
      • 添加到队列中的操作,会自动放到适合的线程中执行

    相关文章

      网友评论

          本文标题:OC高级-NSOperation 和 NSOperationQu

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