iOS开发之多线程技术——NSOperation篇
本篇将从四个方面对iOS开发中使用到的NSOperation技术进行讲解:
一、什么是NSOperation
二、我们为什么使用NSOperation
三、在实际开发中如何使用NSOperation
1、自定义NSOperation
2、NSOperation的基本使用
3、NSOperation实现线程间通信
1)利用代理进行消息传递
2)利用通知实现消息传递
3)利用block进行消息传递
四、与GCD比较
一、什么是NSOperation
NSOperation是一个抽象的基类,表示一个独立的计算单元,可以为子类提供有用且线程安全的建立状态,优先级,依赖和取消等操作。系统已经给我们封装了NSBlockOperation和NSInvocationOperation这两个实体类。使用起来也非常简单,不过我们更多的使用是自己继承并定制自己的操作。
二、我们为什么使用NSOperation
在iOS开发中,为了提升用户体验,我们通常会将操作耗时的操作放在主线程之外的线程进行处理。对于正常的简单操作,我们更多的是选择代码更少的GCD,让我们专注于自己的业务逻辑开发。NSOperation在ios4后也基于GCD实现,但是相对于GCD来说可控性更强,并且可以加入操作依赖。
三、在实际开发中如何使用NSOperation
1、自定义NSOperation
在实际开发中,系统提供的NSOperation可能无法满足我们的需求,这时,我们就需要自定义我们自己的NSOperation
@interface ViewController () @property (nonatomic, weak) IBOutlet UIImageView *imageView; @end @implementation ViewController - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { NSLog(@"touchesBegan------%@", [NSThread currentThread]); if (!self.imageView.image) { // 避免重复下载,增强用户体验 NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue setMaxConcurrentOperationCount:]; MyOperation *myO = [[MyOperation alloc] init];
myO.imageView = self.imageView;
[queue addOperation:myO]; } NSLog(@"end");
} // 自定义NSOperation
@interface MyOperation : NSOperation @property (nonatomic, strong) UIImageView *imageView; @end // 实现自定义NSOperation
@implementation MyOperation - (void)main { NSLog(@"%s----%@", __func__, [NSThread currentThread]); UIImage *image = [self downLoadImage:@"http://g.hiphotos.baidu.com/image/pic/item/f31fbe096b63f624cd2991e98344ebf81b4ca3e0.jpg"]; dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"%@======%@", image, [NSThread currentThread]);
self.imageView.image = image;
});
} - (UIImage *)downLoadImage:(NSString *)urlString {
NSLog(@"%s----%@",__func__, [NSThread currentThread]); NSURL *url = [NSURL URLWithString:urlString];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data]; NSLog(@"图片下载完成"); return image;
} @end
2、NSOperation的基本使用
#pragma mark
#pragma mark - NSOperation高级操作1
- (void)highLevelTest1 {
/**
NSOperation 相对于 GCD 来说,增加了以下管理线程的功能:
1.NSOperation可以添加操作依赖:保证操作的执行顺序! --> 和GCD中将任务添加到一个串行队列中是一样的!一个串行队列会对应一条线程
GCD 中的按顺序执行(串行队列) ---> 串行执行
添加操作依赖之后,系统有可能串行执行保证任务的执行顺序,还有可能绿色线程同步技术,保证任务执行顺序
*/ NSInvocationOperation *inO = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test) object:nil]; NSBlockOperation *block1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"block1======%@", [NSThread currentThread]);
}]; NSBlockOperation *block2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"block2======%@", [NSThread currentThread]);
}]; NSBlockOperation *block3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"block3======%@", [NSThread currentThread]);
}]; /**
四个操作都是耗时操作,并且要求按顺序执行,操作2是UI操作
添加操作依赖的注意点
1.一定要在将操作添加到操作队列中之前添加操作依赖
2.不要添加循环依赖
优点:对于不同操作队列中的操作,操作依赖依然有效
*/ // 1.一定要在将操作添加到操作队列中之前添加操作依赖
[block2 addDependency:block1];
[block3 addDependency:block2];
[inO addDependency:block3];
// 2.不要添加循环依赖
[block1 addDependency:block3]; [[[NSOperationQueue alloc] init] addOperation:block1];
[[[NSOperationQueue alloc] init] addOperation:block2];
[[[NSOperationQueue alloc] init] addOperation:block3]; [[NSOperationQueue mainQueue] addOperation:inO];
} - (void)test {
NSLog(@"测试%s-----%@", __func__, [NSThread currentThread]);
} #pragma mark
#pragma mark - NSOperation高级操作2
- (void)highLevelTest2 {
/**
NSOperation高级操作
应用场景:提高用户体验第一,当用户操作时,取消一切跟用户当前操作无关的进程,提升流畅度
1.添加操作依赖
2.管理操作:重点!是操作队列的方法
2.1暂停/恢复 取消 操作
2.2开启合适的线程数量!(最多不超过6条) 一般开发的时候,会将操作队列设置成一个全局的变量(属性)
*/ NSBlockOperation *block1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"---------");
}]; NSOperationQueue *queue = [[NSOperationQueue alloc] init]; [queue addOperationWithBlock:^{
[self test];
}]; [queue addOperation:block1]; // 1.暂停操作 开始滚动的时候
[queue setSuspended:YES]; // 2.恢复操作 滑动结束的时候
[queue setSuspended:NO]; // 3.取消所有操作 接收到内存警告
[queue cancelAllOperations]; // 3.1补充:取消单个操作调用该NSOperation的cancel方法
[block1 cancel]; // 4.设置线程最大并发数,开启合适的线程数量 实例化操作队列的时候
[queue setMaxConcurrentOperationCount:]; /**
遇到并发编程,什么时候选择 GCD, 什么时候选择NSOperation
1.简单的开启线程/回到主线程,选择GCD:效率更高,简单
2.需要管理操作(考虑到用户交互!)使用NSOperation
*/
} #pragma mark
#pragma mark - NSOperation简单操作
- (void)BaseTest {
// 1.实例化操作对象
NSBlockOperation *blockOperation1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"blockOperation1---------%@", [NSThread currentThread]);
}]; // 往当前操作中追加操作
[blockOperation1 addExecutionBlock:^{
NSLog(@"addblockOperation1.1-----%@", [NSThread currentThread]);
}]; [blockOperation1 addExecutionBlock:^{
NSLog(@"addblockOperation1.2-----%@", [NSThread currentThread]);
}]; /**
当 NSBlockOperation中的任务数 > 1 之后,无论是将操作添加到主线程还是在主线程直接执行 start, NSBlockOperation中的任务执行顺序都不确定,执行线程也不确定!
一般在开发的时候,要避免向 NSBlockOperation 中追加任务!
如果任务都是在子线程中执行,并且不需要保证执行顺序!可以直接追加任务
*/ NSBlockOperation *blockOperation2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"blockOperation2---------%@", [NSThread currentThread]);
}]; NSBlockOperation *blockOperation3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"blockOperation3---------%@", [NSThread currentThread]);
}]; // 将操作添加到非主队列中
// NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//
// [queue addOperation:blockOperation1];
// [queue addOperation:blockOperation2];
// [queue addOperation:blockOperation3]; // 将操作添加到主队列中
[[NSOperationQueue mainQueue] addOperation:blockOperation1];
[[NSOperationQueue mainQueue] addOperation:blockOperation2];
[[NSOperationQueue mainQueue] addOperation:blockOperation3];
}
3、NSOperation实现线程间通信
1)利用代理进行消息传递
@interface ViewController ()<MyOperationDelegate> @property (weak, nonatomic) IBOutlet UIImageView *imageView; @end @implementation ViewController - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { NSLog(@"%s-----%@", __func__, [NSThread currentThread]); NSString *urlString = @"http://h.hiphotos.baidu.com/image/pic/item/30adcbef76094b366b2389d7a4cc7cd98d109d53.jpg"; // 1.创建操作对象
MyOperation *myO = [[MyOperation alloc] init]; myO.delegate = self; // 3.告诉 myO 下载哪一张图片
myO.urlString = urlString; NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue setMaxConcurrentOperationCount:]; // 4.将操作添加到操作队列中
[queue addOperation:myO]; NSLog(@"end");
} #pragma mark
#pragma mark - 实现操作的代理方法
- (void)downImage:(UIImage *)image {
NSLog(@"%s------%@", __func__, [NSThread currentThread]);
self.imageView.image = image;
} @protocol MyOperationDelegate <NSObject> - (void)downImage:(UIImage *)image; @end @interface MyOperation : NSOperation @property (nonatomic, copy) NSString *urlString; @property (nonatomic, weak) id<MyOperationDelegate> delegate; @end @implementation MyOperation #pragma mark
#pragma mark - 重写NSOperation的main方法
// 当把自定义的操作添加到操作队列中,或者直接调用操作的 start 方法后,都会自动来执行main 方法中的内容
- (void)main {
NSLog(@"%s------%@", __func__, [NSThread currentThread]); UIImage *image = [self downLoadImageSubThread]; // 回到主线程执行代理方法
dispatch_async(dispatch_get_main_queue(), ^{
if ([self.delegate respondsToSelector:@selector(downImage:)]) {
[self.delegate downImage:image];
}
}); } #pragma mark
#pragma mark - 下载网络图片的方法
- (UIImage *)downLoadImageSubThread { NSURL *url = [NSURL URLWithString:self.urlString]; NSData *data = [NSData dataWithContentsOfURL:url]; NSLog(@"下载完成");
return [UIImage imageWithData:data]; } @end
2)利用通知实现消息传递
@interface ViewController () @property (nonatomic, weak) IBOutlet UIImageView *imageView04; @end @implementation ViewController - (void)viewDidLoad {
NSLog(@"%s-----%@", __func__, [NSThread currentThread]);
// 注册通知观察者
// object:nil ,nil可以保证观察者接收任意类型的通知!
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(setUpImageWithNotify:) name:@"FinishDownLoadImage" object:nil];
} - (void)dealloc {
// 通知用完之后需要移除
[[NSNotificationCenter defaultCenter] removeObserver:self];
} // 接收到通知之后,执行的方法 noti:接收到的通知
- (void)setUpImageWithNotify:(NSNotification *)noti {
NSLog(@"%s-----%@", __func__, [NSThread currentThread]);
// 显示图片
self.imageView04.image = noti.object;
} - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { NSLog(@"%s-----%@", __func__, [NSThread currentThread]); // 1.创建操作队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue setMaxConcurrentOperationCount:]; // 2.告诉操作下载哪张图片
MyOperation *myO = [[MyOperation alloc] init]; myO.urlString = @"http://h.hiphotos.baidu.com/image/pic/item/72f082025aafa40f7c884d31af64034f79f0198b.jpg"; // 3.将操作添加到操作队列,会自动调用操作中的main 方法
[queue addOperation:myO]; NSLog(@"end");
} /**************************************************/
// 自定义NSOperation类
@interface MyOperation : NSOperation @property (nonatomic, copy) NSString *urlString; @end // 实现自定义NSOperation类
@implementation MyOperation - (void)main {
NSLog(@"%s-----%@", __func__, [NSThread currentThread]); // 在子线程下载好图片后再传给主线程
UIImage *image = [self downLoadImage:self.urlString]; // 图片下载完毕之后,利用通知告诉控制器,图片下载结束,并且将下载好的图片传递给控制器
// 在主线程发送通知
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:@"FinishDownLoadImage" object:image];
}); } - (UIImage *)downLoadImage:(NSString *)urlString { NSURL *url = [NSURL URLWithString:urlString]; NSData *data = [NSData dataWithContentsOfURL:url]; return [UIImage imageWithData:data]; }
3)利用block进行消息传递
@interface ViewController () @property (weak, nonatomic) IBOutlet UIImageView *imageView; @end @implementation ViewController - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { NSLog(@"%s----%@",__func__, [NSThread currentThread]); NSString *urlString = @"http://h.hiphotos.baidu.com/image/pic/item/72f082025aafa40f7c884d31af64034f79f0198b.jpg"; // 2.创建操作
MyOperation *mo = [[MyOperation alloc] init];
mo.urlString = urlString; mo.view = self.view; [mo downLoadWebImageWithBlock:^(UIImage *image) {
self.imageView.image = image;
}]; [mo setCompletionBlock:^{
NSLog(@"图片下载完成");
}]; NSLog(@"end"); } /*********************************************************/
// 自定义NSOperation类
typedef void(^downLoadBlock)(UIImage *image); @interface MyOperation : NSOperation @property (nonatomic, strong) UIView *view; @property (nonatomic, copy) NSString *urlString; @property (nonatomic, copy) downLoadBlock block; - (void)downLoadWebImageWithBlock:(downLoadBlock)blk; @end // 实现自定义NSOperation类
@implementation MyOperation #pragma mark
#pragma mark - 实现block方法,方便直接回车调用
- (void)downLoadWebImageWithBlock:(downLoadBlock)blk {
if (blk) {
self.block = blk; dispatch_async(dispatch_get_global_queue(, ), ^{
UIImage *image = [self downLoadImage:self.urlString]; dispatch_async(dispatch_get_main_queue(), ^{ if (self.block) {
self.block(image);
} });
});
} } #pragma mark
#pragma mark - 下载图片的方法
- (UIImage *)downLoadImage:(NSString *)strUrl { NSLog(@"%s----%@",__func__, [NSThread currentThread]); NSURL *url = [NSURL URLWithString:strUrl]; NSData *data = [NSData dataWithContentsOfURL:url]; NSLog(@"下载完成"); return [UIImage imageWithData:data]; } @end
四、与GCD比较
GCD:
将任务(block)添加到队列(串行/并发/主队列),并且指定任务执行的函数(同步/异步)
GCD是底层的C语言构成的API
iOS 4.0 推出的,针对多核处理器的并发技术
在队列中执行的是由 block 构成的任务,这是一个轻量级的数据结构
要停止已经加入 queue 的 block 需要写复杂的代码
需要通过 Barrier 或者同步任务设置任务之间的依赖关系
只能设置队列的优先级
高级功能:
一次性 once
延迟操作 after
调度组
NSOperation:
核心概念:把操作(异步)添加到队列(全局的并发队列)
OC 框架,更加面向对象,是对 GCD 的封装
iOS 2.0 推出的,苹果推出 GCD 之后,对 NSOperation 的底层全部重写
Operation作为一个对象,为我们提供了更多的选择
可以随时取消已经设定要准备执行的任务,已经执行的除外
可以跨队列设置操作的依赖关系
可以设置队列中每一个操作的优先级
高级功能:
最大操作并发数(GCD不好做)
继续/暂停/全部取消
跨队列设置操作的依赖关系
iOS开发之多线程技术——NSOperation篇的更多相关文章
- iOS开发之多线程技术——GCD篇
本篇将从四个方面对iOS开发中GCD的使用进行详尽的讲解: 一.什么是GCD 二.我们为什么要用GCD技术 三.在实际开发中如何使用GCD更好的实现我们的需求 一.Synchronous & ...
- iOS开发之多线程技术—GCD篇
本篇将从四个方面对iOS开发中GCD的使用进行详尽的讲解: 一.什么是GCD 二.我们为什么要用GCD技术 三.在实际开发中如何使用GCD更好的实现我们的需求 一.Synchronous & ...
- iOS开发之多线程技术
本篇争取一篇讲清讲透,依然将通过四大方面清晰的对iOS开发中多线程的用法进行详尽的讲解: 一.什么是多线程 1)多线程执行原理 2)线程与进程 3)多线程的优缺点 二.我们为什么要用多线程编程技术 三 ...
- iOS开发——网络使用技术OC篇&网络爬虫-使用正则表达式抓取网络数据
网络爬虫-使用正则表达式抓取网络数据 关于网络数据抓取不仅仅在iOS开发中有,其他开发中也有,也叫网络爬虫,大致分为两种方式实现 1:正则表达 2:利用其他语言的工具包:java/Python 先来看 ...
- 【iOS开发】多线程下NSOperation、NSBlockOperation、NSInvocationOperation、NSOperationQueue的使用
http://blog.csdn.net/crycheng/article/details/21799611 本篇文章主要介绍下多线程下NSOperation.NSBlockOperation.NSI ...
- iOS开发之多线程技术(NSThread、OperationQueue、GCD)
在前面的博客中如果用到了异步请求的话,也是用到的第三方的东西,没有正儿八经的用过iOS中多线程的东西.其实多线程的东西还是蛮重要的,如果对于之前学过操作系统的小伙伴来说,理解多线程的东西还是比较容易的 ...
- iOS开发:多线程技术概述
一.概述 线程(thread):用于指代独立执行的代码段. 进程(process):用于指代一个正在运行的可执行程序,它可以包含多个线程. 任务(task):用于指代抽象的概念,表示需要执行工作. 多 ...
- iOS开发-多线程编程技术(Thread、Cocoa operations、GCD)
简介 在软件开发中,多线程编程技术被广泛应用,相信多线程任务对我们来说已经不再陌生了.有了多线程技术,我们可以同做多个事情,而不是一个一个任务地进行.比如:前端和后台作交互.大任务(需要耗费一定的时间 ...
- iOS开发之多线程(NSThread、NSOperation、GCD)
整理一些多线程相关的知识. 并行 & 并发 1.并行:并行是相对于多核而言的,几个任务同时执行.2.并发:并发是相对于单核而言的,几个任务之间快速切换运行,看起来像是"同时" ...
随机推荐
- Git 查看文件的历史
用惯了tfs,刚一接触git感觉很不顺手,特别是一些很基本的操作,用起来都怪怪的(可能是不习惯命令行的原因吧).下面把查看文件历史的用例小结一下. 查看某个文件的修改历史 在git中查看历史的命令主要 ...
- 一行代码实现js数组去重
console.log([...new Set([1,2,3,4,2,1,2,2,2,167,4,3,32,2,1])])
- C#根据网址生成静态页面
HoverTree开源项目中HoverTreeWeb.HVTPanel的Index.aspx文件 是后台管理的首页. 包含生成留言板首页,以及显示用户名,退出等功能. 根据网址生成页面的方法: boo ...
- C#连接Excel示例代码和驱动
代码如下: string fileExt = Path.GetExtension(excelPath); string conn = ""; if (fileExt == &quo ...
- 局部(或全局)设置<a>标签的target属性
对于超链接<a>标签,target属性的设置是比较关键的,在不同的用户场景下选用适合的新页面载入方式,可以大大的提高访客的体验感.我们一般对target的设置可以挨个来,但 ...
- EF生成 类型“System.Data.Entity.DbContext”在未被引用的程序集中定义
错误描述: 1 类型“System.Data.Entity.DbContext”在未被引用的程序集中定义.必须添加对程序集“EntityFramework, Version=5.0.0.0, Cult ...
- 介绍开源的.net通信框架NetworkComms框架 源码分析(十)DOSProtection
原文网址: http://www.cnblogs.com/csdev Networkcomms 是一款C# 语言编写的TCP/UDP通信框架 作者是英国人 以前是收费的 目前作者已经开源 许可是 ...
- UrlRewriter实现.NET的URL重写
首先下载安装MSDNURLRewriting.msi,链接地址http://pan.baidu.com/s/1c0pRnTu. 在安装目录下找到RewriterTester\bin\URLRewrit ...
- [测试] Firemonkey Android 照相自订分辨率测试
在 Delphi 10 Seattle 提供了照相及相册自订分辨率的功能,请见官方网站教学: http://docwiki.embarcadero.com/RADStudio/Seattle/en/T ...
- 迷信again
当在VirtualBox中尝试安装Debian 8.3.0 三次都失败后 - 每次卡在安装软件这一步,我决定不再迷信Debian软件包质量高这件事.