• 3.多线程NSOperation


    1.NSOperation的基本操作

    使用NSOperation的两个子类,NSInvocationOperation 和 NSBlockOperation 创建操作,然后将操作添加到队列中去执行

    // NSOperation
        
        // 1. 实例化 NSOperation 子类对象:NSInvocationOperation
        NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test1) object:nil];
        
        // 2. 实例化 NSOperation 子类对象
        NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
            //
            NSLog(@"耗时操作2------------");
        }];
        
        
        // NSOperationQueue
        
        // 1.获取主队列
        NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
        
        
        // 2.创建非主队列
        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        
        // NSOperation使用: 将操作添加到队列中!
        [mainQueue addOperation:op];
        
        [queue addOperation:op1];

    2.NSOperation定义的操作可以直接用start启动,相当于直接执行,入口是NSOperation中定义的main方法

        // 1. 实例化操作对象
        NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test1) object:nil];
        
        NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test2) object:nil];
    
        NSInvocationOperation *op3 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test3) object:nil];
        
        
        // 需求: 1. 三个操作都是耗时操作!
        
    //    // 2. 创建非主队列
    //    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    //    
    //    // 3. 将操作添加到非主队列中
    //    [queue addOperation:op1];
    //    [queue addOperation:op2];
    //    [queue addOperation:op3];
        
    //    // 需求: 2. 三个操作都是UI操作
    //    NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
    //    // 相当于 GCD 中的 异步函数 + 主队列!
    //    [mainQueue addOperation:op1];
    //    [mainQueue addOperation:op2];
    //    [mainQueue addOperation:op3];
        
        // NSOperation 执行方式2: 直接启动!直接在当前线程执行!
        [op1 start];
        [op2 start];
        [op3 start];
        
        // NSOperation 对象的入口是定义在自身内部的 main 方法;
        // 当将操作添加到操作队列中或者直接调用操作的 start 方法之后,内部都会调用 main 方法,所有两者都能够执行操作!
    
        
        NSLog(@"touchesEnd");
        
    }
    
    - (void)test1
    {
        NSLog(@"test1----------%@",[NSThread currentThread]);
    }
    
    - (void)test2
    {
        NSLog(@"test2----------%@",[NSThread currentThread]);
    }
    
    
    - (void)test3
    {
        NSLog(@"test3----------%@",[NSThread currentThread]);
    }

    3.使用block

    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
            
            NSLog(@"222222222----%@",[NSThread currentThread]);
        }];
        
        NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
            
            NSLog(@"33333333----%@",[NSThread currentThread]);
        }];
        
        
        // 1.将操作添加到主队列中
        //    [[NSOperationQueue mainQueue] addOperation:op1];
        //    [[NSOperationQueue mainQueue] addOperation:op2];
        //    [[NSOperationQueue mainQueue] addOperation:op3];
        
        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        [queue addOperation:op1];
        [queue addOperation:op2];
        [queue addOperation:op3];

    4.向blockOperation中追加任务

        // 当 NSBlockOperation中的任务数 > 1 之后, 无论是将操作添加到主线程,还是在主线程直接执行 start ,NSBlockOperation中的任务执行顺序都不确定,执行线程也不确定!

        // 一半在开发的时候,要避免向 NSBlockOperation 中追加任务!

        // 如果任务都是在子线程中执行,并且不需要保证执行顺序,可以直接追加任务!

    // 1. 实例化操作对象
        NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
            
            NSLog(@"11111111----%@",[NSThread currentThread]);
        }];
        
        // 往当前操作中追加操作
        [op1 addExecutionBlock:^{
            
            NSLog(@"追加任务1%@",[NSThread currentThread]);
            
        }];
        
        // 往当前操作中追加操作
        [op1 addExecutionBlock:^{
            
            NSLog(@"追加任务2%@",[NSThread currentThread]);
            
        }];
        
        // 当 NSBlockOperation中的任务数 > 1 之后, 无论是将操作添加到主线程,还是在主线程直接执行 start ,NSBlockOperation中的任务执行顺序都不确定,执行线程也不确定!
        // 一半在开发的时候,要避免向 NSBlockOperation 中追加任务!
        // 如果任务都是在子线程中执行,并且不需要保证执行顺序,可以直接追加任务!

    5.直接通过操作队列添加任务

    // 直接通过操作队列添加任务!
        
        //将block中的内容当做一个操作添加到主队列中!
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            
            NSLog(@"---------1%@",[NSThread currentThread]);
            
        }];
        
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            
            NSLog(@"---------2%@",[NSThread currentThread]);
            
        }];
        
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            
            NSLog(@"---------3%@",[NSThread currentThread]);
            
        }];
        
        
        [[[NSOperationQueue alloc] init] addOperationWithBlock:^{
            
            NSLog(@"---------4%@",[NSThread currentThread]);
    
        }];
        
        [[[NSOperationQueue alloc] init] addOperationWithBlock:^{
            
            NSLog(@"---------5%@",[NSThread currentThread]);
            
        }];
    
        
        [[[NSOperationQueue alloc] init] addOperationWithBlock:^{
            
            NSLog(@"---------6%@",[NSThread currentThread]);
            
        }];

    6.给操作添加操作依赖,保证操作的顺序执行,避免循环依赖

    // NSOperation 相对于 GCD 来说,增加了以下管理线程的功能:
        // 1. NSOperation可以添加操作依赖: 保证操作的执行顺序!  --> 和GCD中将任务添加到一个串行队列中是一样的! 一个串行队列会对应一条线程!
        
        // GCD 中的按顺序执行(串行队列)----> 串行执行!
        // 添加操作依赖之后,系统有可能串行执行保证任务的执行顺序,还有可能采用线程同步技术,保证任务执行顺序!
        
        
        NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test) object:nil];
        
        
        NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
            
            NSLog(@"-------222 %@",[NSThread currentThread]);
            
        }];
        
        NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
            
            NSLog(@"-------333 %@",[NSThread currentThread]);
            
        }];
        
        NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
            
            NSLog(@"-------444 %@",[NSThread currentThread]);
            
        }];
        
    
        // 四个操作都是耗时操作! 并且要求按顺序执行! 操作2是UI操作!
        
        // 添加操作依赖的注意点:
        // 1. 不要添加循环依赖!
        // 2. 一定要在将操作添加到操作队列中之前添加操作依赖!
        
        // 优点: 对于不同操作队列中的操作,操作依赖依然有效!
        
        // 添加操作依赖!
        [op2 addDependency:op1];
        
        [op3 addDependency:op2];
        
        [op4 addDependency:op3];
        
    
        
       // [op2 addDependency:op3];
       // [op1 addDependency:op4];
        
        
        // 将操作添加到操作队列中
        NSOperationQueue *quque = [[NSOperationQueue alloc] init];
        
        [quque addOperation:op3];
        [quque addOperation:op1];
        [quque addOperation:op4];
        
        // 将操作2 添加到主队列中
        [[NSOperationQueue mainQueue] addOperation:op2];

    7.NSOperation的高级操作,暂停/恢复/取消/设置最大线程数 

      // 遇到并发编程,什么时候选择 GCD ,什么时候选择 NSOperation!

        // 1.简单的开启线程/回到主线程,选择 GCD : 效率更高,简单!

        // 2.需要管理操作(考虑到与用户交互!),使用 NSOperation!

     // NSOperation高级操作:
        
        // 1. 添加操作依赖!
        // 2. 管理操作: 重点! 是操作队列的方法!
        // 2.1 暂停/恢复 取消 操作!
        // 2.2 开启合适的线程数量!(最多不超过6条!)
        
        // 一半开发的时候,会将操作队列设置成一个全局的变量(属性)!
        //
        
        NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
            
            NSLog(@"0----------");
            
            [self test];
        }];
        
        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        
        [queue addOperationWithBlock:^{
            
            [self test];
        }];
        
        [queue addOperation:op];
        
        // 1. 暂停操作// 开始滚动的时候
        [queue setSuspended:YES];
    
        // 2. 恢复操作// 滚动结束的时候
        [queue setSuspended:NO];
        
        
        // 3. 取消所有操作// 接收到内存警告
        [queue cancelAllOperations];
        
        // 3补充: 取消单个操作!是操作的方法!
        [op cancel];
        
        // 设置最大并发数,开启合适的线程数量 // 实例化操作队列的时候
        [queue setMaxConcurrentOperationCount:6];
        
        // 遇到并发编程,什么时候选择 GCD ,什么时候选择 NSOperation!
        // 1.简单的开启线程/回到主线程,选择 GCD : 效率更高,简单!
        // 2.需要管理操作(考虑到与用户交互!),使用 NSOperation!

    8.block的循环引用问题,使用__weak typeof(self)weakself = self;弱引用

    #import "HMViewController.h"
    
    @interface HMViewController ()
    
    // 全局操作队列
    @property(nonatomic ,strong)NSOperationQueue *queue;
    
    //
    @property(nonatomic ,strong)NSMutableArray *array;
    
    @end
    
    @implementation HMViewController
    
    -(NSOperationQueue *)queue
    {
        if (!_queue) {
            
            _queue = [[NSOperationQueue alloc] init];
            
            [_queue setMaxConcurrentOperationCount:6];
        }
        
        return _queue;
    }
    
    -(NSMutableArray *)array
    {
        if (!_array) {
            _array = [NSMutableArray array];
        }
        return _array;
    }
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        self.view.backgroundColor = [UIColor orangeColor];
        //
        NSLog(@"控制器创建成功!");
    
    }
    
    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
    {
        NSLog(@"touchesBegan");
        // block!
        
        // 1. 创建操作
        
        // 为了防止 block 中使用 self 出现的循环引用问题! 一般在 block中使用 self 的时候,要使用 self 的弱引用!!!
        
        // 为了安全,block中出现self,都使用 弱引用写法!
        
        // weakself : 下面就是 self 的弱引用写法!
        __weak typeof(self)weakself = self;
        
        // __weak HMViewController *wself = self;
        NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
            
            [self test];
        }];
    
        // 将操作添加到 array 中!
        [self.array addObject:op];
        
        // self --> queue -->op --> block --> self  : 循环引用链条!
        
        
        // 2. 将操作添加到操作队列中
        // 因为操作执行完毕之后,操作队列会自动释放其中的操作,所以,在操作中(NSblockOperation)block里有了 self,没有关系,能够释放.不会造成循环引用!
        [self.queue addOperation:op];
        
        
    }
    
    -(void)test
    {
        NSLog(@"------%@",[NSThread currentThread]);
    }
    
    -(void)dealloc
    {
        NSLog(@"控制器销毁了!");
    }

    9.线程间通信,在子线程下载图片,在主线程更新UI

    // 1 在子线程下载图片
        
        NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
            
            // 如果图片地址中出现了 & 符号,换一张图片!
            // image :下载好的网络图片
            UIImage *image = [self downloadWebImageWithUrlString:@"http://pic14.nipic.com/20110522/7411759_164157418126_2.jpg"];
            
        
            // 回到主线程!
            [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                
                // 显示图片
                self.imageView.image = image;
            }];
        }];
        
        
        [self.queue addOperation:op];
        
        NSLog(@"touchesEnd");
        
    }
    
    
    // 下载网络图片的方法
    - (UIImage *)downloadWebImageWithUrlString:(NSString *)urlString
    {
        NSURL *url = [NSURL URLWithString:urlString];
        
        // 下载方法!耗时方法!
        NSData *data = [NSData dataWithContentsOfURL:url];
        
        UIImage *image = [UIImage imageWithData:data];
        
        return image;
    }

     10.自定义NSOperation,继承NSOperation,重写mian方法

    加自动释放池

    //
    //  HMDownloadOperation.h
    //  09-线程间通信
    //
    //  Created by HM on 16/1/25.
    //  Copyright © 2016年 HM. All rights reserved.
    //
    
    #import <Foundation/Foundation.h>
    #import <UIKit/UIKit.h>
    
    @class HMDownloadOperation;
    
    @protocol HMDownloadOperationDelegate <NSObject>
    
    
    -(void)downloadImageWithOperation:(HMDownloadOperation *)operation;
    
    @end
    
    @interface HMDownloadOperation : NSOperation
    
    // 代理属性
    @property(nonatomic ,weak) id<HMDownloadOperationDelegate> delegate;
    
    // 写一个图片属性// 下载好的图片
    @property(nonatomic ,strong) UIImage *image;
    
    // 图片下载地址
    @property(nonatomic ,copy)NSString *urlString;
    
    
    
    @end
    //
    //  HMDownloadOperation.m
    //  09-线程间通信
    //
    //  Created by HM on 16/1/25.
    //  Copyright © 2016年 HM. All rights reserved.
    //
    
    #import "HMDownloadOperation.h"
    
    @implementation HMDownloadOperation
    
    // 重写 NSOperation 的 main 方法!
    // 当把自定义的操作添加到操作队列中,或者直接调用 操作的  start 方法之后,都会自动来执行 main 方法中的内容!
    -(void)main
    {
        // 为了能够及时释放内存,一般会手动书写一个 autoreleasepool!苹果官方文档不要求写!
        @autoreleasepool {
            
            NSLog(@"----------%@",[NSThread currentThread]);
            
            self.image = [self downloadWebImageWithUrlString:self.urlString];
            
            NSLog(@"image:%@",self.image);
            
            // 通知/代理/block :为了保证在不同对象之间传值的准确性!采用的是同步传值的方法!
            
            // 回到主线程,执行代理方法:
            dispatch_async(dispatch_get_main_queue(), ^{
                // 执行代理方法
                if ([self.delegate respondsToSelector:@selector(downloadImageWithOperation:)]) {
                    [self.delegate downloadImageWithOperation:self];
                }
            });
        }
    }
    
    
    // 下载网络图片的方法
    - (UIImage *)downloadWebImageWithUrlString:(NSString *)urlString
    {
        NSURL *url = [NSURL URLWithString:urlString];
        
        // 下载方法!耗时方法!
        NSData *data = [NSData dataWithContentsOfURL:url];
        
        UIImage *image = [UIImage imageWithData:data];
        
        return image;
    }
    
    @end

    11.操作完成之后的回调completionBlock

    NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
            
            NSLog(@"--------%@",[NSThread currentThread]);
            
        }];
        
        // 操作完成之后的回调!
    //    op.completionBlock = ^(){
    //     
    //        NSLog(@"操作执行完毕!");
    //        
    //    };
        [op setCompletionBlock:^{
            
            NSLog(@"操作执行完毕!");
        }];
        
        
        [op start];
  • 相关阅读:
    java Object类源代码详解 及native (转自 http://blog.csdn.net/sjw890821sjw/article/details/8058843)
    使用库项目开发
    Android 开发规范
    使用Git之后出现android library引用失败
    Github安卓开源项目编译运行
    android项目引入三方类库配置文件
    Web交互设计优化的简易check list
    php程序效率优化的一些策略小结
    SQL语句优化原则
    纯PHP实现定时器任务(Timer)
  • 原文地址:https://www.cnblogs.com/fanglove/p/5216661.html
Copyright © 2020-2023  润新知