热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

3.多线程NSOperation

1.NSOperation的基本操作使用NSOperation的两个子类,NSInvocationOperation和NSBlockOperation创建操作,然后将操作添加到队列

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 *)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 
#import 

@class HMDownloadOperation;

@protocol HMDownloadOperationDelegate 


-(void)downloadImageWithOperation:(HMDownloadOperation *)operation;

@end

@interface HMDownloadOperation : NSOperation

// 代理属性
@property(nonatomic ,weak) id 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];

3.多线程NSOperation


推荐阅读
  • 基于layUI的图片上传前预览功能的2种实现方式
    本文介绍了基于layUI的图片上传前预览功能的两种实现方式:一种是使用blob+FileReader,另一种是使用layUI自带的参数。通过选择文件后点击文件名,在页面中间弹窗内预览图片。其中,layUI自带的参数实现了图片预览功能。该功能依赖于layUI的上传模块,并使用了blob和FileReader来读取本地文件并获取图像的base64编码。点击文件名时会执行See()函数。摘要长度为169字。 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • 本文讨论了Alink回归预测的不完善问题,指出目前主要针对Python做案例,对其他语言支持不足。同时介绍了pom.xml文件的基本结构和使用方法,以及Maven的相关知识。最后,对Alink回归预测的未来发展提出了期待。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • Mac OS 升级到11.2.2 Eclipse打不开了,报错Failed to create the Java Virtual Machine
    本文介绍了在Mac OS升级到11.2.2版本后,使用Eclipse打开时出现报错Failed to create the Java Virtual Machine的问题,并提供了解决方法。 ... [详细]
  • t-io 2.0.0发布-法网天眼第一版的回顾和更新说明
    本文回顾了t-io 1.x版本的工程结构和性能数据,并介绍了t-io在码云上的成绩和用户反馈。同时,还提到了@openSeLi同学发布的t-io 30W长连接并发压力测试报告。最后,详细介绍了t-io 2.0.0版本的更新内容,包括更简洁的使用方式和内置的httpsession功能。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • 后台获取视图对应的字符串
    1.帮助类后台获取视图对应的字符串publicclassViewHelper{将View输出为字符串(注:不会执行对应的ac ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
  • 本文介绍了通过ABAP开发往外网发邮件的需求,并提供了配置和代码整理的资料。其中包括了配置SAP邮件服务器的步骤和ABAP写发送邮件代码的过程。通过RZ10配置参数和icm/server_port_1的设定,可以实现向Sap User和外部邮件发送邮件的功能。希望对需要的开发人员有帮助。摘要长度:184字。 ... [详细]
  • 本文介绍了关于apache、phpmyadmin、mysql、php、emacs、path等知识点,以及如何搭建php环境。文章提供了详细的安装步骤和所需软件列表,希望能帮助读者解决与LAMP相关的技术问题。 ... [详细]
  • 高质量SQL书写的30条建议
    本文提供了30条关于优化SQL的建议,包括避免使用select *,使用具体字段,以及使用limit 1等。这些建议是基于实际开发经验总结出来的,旨在帮助读者优化SQL查询。 ... [详细]
  • 如何在服务器主机上实现文件共享的方法和工具
    本文介绍了在服务器主机上实现文件共享的方法和工具,包括Linux主机和Windows主机的文件传输方式,Web运维和FTP/SFTP客户端运维两种方式,以及使用WinSCP工具将文件上传至Linux云服务器的操作方法。此外,还介绍了在迁移过程中需要安装迁移Agent并输入目的端服务器所在华为云的AK/SK,以及主机迁移服务会收集的源端服务器信息。 ... [详细]
  • 本文详细介绍了MySQL表分区的创建、增加和删除方法,包括查看分区数据量和全库数据量的方法。欢迎大家阅读并给予点评。 ... [详细]
  • 猜字母游戏
    猜字母游戏猜字母游戏——设计数据结构猜字母游戏——设计程序结构猜字母游戏——实现字母生成方法猜字母游戏——实现字母检测方法猜字母游戏——实现主方法1猜字母游戏——设计数据结构1.1 ... [详细]
author-avatar
马丁乐_449
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有