多线程编程2-NSOperation
- 前言
- 一、NSInvocationOperation
- 二、NSBlockOperation
- 三、NSOperation的其他用法
- 四、自定义NSOperation
前言
1.上一讲简单介绍了NSThread的使用,虽然也可以实现多线程编程,但是需要我们去管理线程的生命周期,还要考虑线程同步、加锁问题,造成一些性能上的开销。我们也可以配合使用NSOperation和NSOperationQueue实现多线程编程,实现步骤大致是这样的:
1> 先将需要执行的操作封装到一个NSOperation对象中
2> 然后将NSOperation对象添加到NSOperationQueue中
3> 系统会自动将NSOperation中封装的操作放到一条新线程中执行
在此过程中,我们根本不用考虑线程的生命周期、同步、加锁等问题
下面列举一个应用场景,比如微博的粉丝列表:
每一行的头像肯定要从新浪服务器下载图片后才能显示的,而且是需要异步下载。这时候你就可以把每一行的图片下载操作封装到一个NSOperation对象中,上面有6行,所以要创建6个NSOperation对象,然后添加到NSOperationQueue中,分别下载不同的图片,下载完毕后,回到对应的行将图片显示出来。
2.默认情况下,NSOperation并不具备封装操作的能力,必须使用它的子类,使用NSOperation子类的方式有3种:
1> NSInvocationOperation
2> NSBlockOperation
3> 自定义子类继承NSOperation,实现内部相应的方法
这讲先介绍如何用NSOperation封装一个操作,后面再结合NSOperationQueue来使用。
一、NSInvocationOperation
1 NSInvocationOperation *operation = [[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run:) object:@"mj"] autorelease]; 2 [operation start];
* 第1行初始化了一个NSInvocationOperation对象,它是基于一个对象和selector来创建操作
* 第2行调用了start方法,紧接着会马上执行封装好的操作,也就是会调用self的run:方法,并且将@"mj"作为方法参数
* 这里要注意:默认情况下,调用了start方法后并不会开一条新线程去执行操作,而是在当前线程同步执行操作。只有将operation放到一个NSOperationQueue中,才会异步执行操作。
二、NSBlockOperation
1.同步执行一个操作
1 NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^(){ 2 NSLog(@"执行了一个新的操作"); 3 }]; 4 // 开始执行任务 5 [operation start];
* 第1行初始化了一个NSBlockOperation对象,它是用一个Block来封装需要执行的操作
* 第2行调用了start方法,紧接着会马上执行Block中的内容
* 这里还是在当前线程同步执行操作,并没有异步执行
2.并发执行多个操作
![](/assets/blank.gif)
1 NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^(){2 NSLog(@"执行第1次操作,线程:%@", [NSThread currentThread]);3 }];4 5 [operation addExecutionBlock:^() {6 NSLog(@"又执行了1个新的操作,线程:%@", [NSThread currentThread]);7 }];8 9 [operation addExecutionBlock:^() { 10 NSLog(@"又执行了1个新的操作,线程:%@", [NSThread currentThread]); 11 }]; 12 13 [operation addExecutionBlock:^() { 14 NSLog(@"又执行了1个新的操作,线程:%@", [NSThread currentThread]); 15 }]; 16 17 // 开始执行任务 18 [operation start];
![](/assets/blank.gif)
* 第1行初始化了一个NSBlockOperation对象
* 分别在第5、9、13行通过addExecutionBlock:方法添加了新的操作,包括第1行的操作,一共封装了4个操作
* 在第18行调用start方法后,就会并发地执行这4个操作,也就是会在不同线程中执行
1 2013-02-02 21:38:46.102 thread[4602:c07] 又执行了1个新的操作,线程:<NSThread: 0x7121d50>{name = (null), num = 1} 2 2013-02-02 21:38:46.102 thread[4602:3f03] 又执行了1个新的操作,线程:<NSThread: 0x742e1d0>{name = (null), num = 5} 3 2013-02-02 21:38:46.102 thread[4602:1b03] 执行第1次操作,线程:<NSThread: 0x742de50>{name = (null), num = 3} 4 2013-02-02 21:38:46.102 thread[4602:1303] 又执行了1个新的操作,线程:<NSThread: 0x7157bf0>{name = (null), num = 4}
可以看出,每个操作所在线程的num值都不一样,说明是不同线程
三、NSOperation的其他用法
1.取消操作
operation开始执行之后, 默认会一直执行操作直到完成,我们也可以调用cancel方法中途取消操作
[operation cancel];
2.在操作完成后做一些事情
如果想在一个NSOperation执行完毕后做一些事情,就调用NSOperation的setCompletionBlock方法来设置想做的事情
operation.completionBlock = ^() {NSLog(@"执行完毕"); };
当operation封装的操作执行完毕后,就会回调Block里面的内容
四、自定义NSOperation
如果NSInvocationOperation和NSBlockOperation不能满足需求,我们可以直接新建子类继承NSOperation,并添加任何需要执行的操作。如果只是简单地自定义NSOperation,只需要重载-(void)main这个方法,在这个方法里面添加需要执行的操作。
下面写个子类DownloadOperation来下载图片
1.继承NSOperation,重写main方法
DownloadOperation.h
![](/assets/blank.gif)
#import <Foundation/Foundation.h> @protocol DownloadOperationDelegate;@interface DownloadOperation : NSOperation // 图片的url路径 @property (nonatomic, copy) NSString *imageUrl; // 代理 @property (nonatomic, assign) id<DownloadOperationDelegate> delegate;- (id)initWithUrl:(NSString *)url delegate:(id<DownloadOperationDelegate>)delegate; @end// 图片下载的协议 @protocol DownloadOperationDelegate <NSObject> - (void)downloadFinishWithImage:(UIImage *)image; @end
![](/assets/blank.gif)
DownloadOperation.m
![](/assets/blank.gif)
1 #import "DownloadOperation.h"2 3 @implementation DownloadOperation4 @synthesize delegate = _delegate;5 @synthesize imageUrl = _imageUrl;6 7 // 初始化8 - (id)initWithUrl:(NSString *)url delegate:(id<DownloadOperationDelegate>)delegate {9 if (self = [super init]) { 10 self.imageUrl = url; 11 self.delegate = delegate; 12 } 13 return self; 14 } 15 // 释放内存 16 - (void)dealloc { 17 [super dealloc]; 18 [_imageUrl release]; 19 } 20 21 // 执行主任务 22 - (void)main { 23 // 新建一个自动释放池,如果是异步执行操作,那么将无法访问到主线程的自动释放池 24 @autoreleasepool { 25 // .... 26 } 27 } 28 @end
![](/assets/blank.gif)
* 在第22行重载了main方法,等会就把下载图片的代码写到这个方法中
* 如果这个DownloadOperation是在异步线程中执行操作,也就是说main方法在异步线程调用,那么将无法访问主线程的自动释放池,所以在第24行创建了一个属于当前线程的自动释放池
2.正确响应取消事件
* 默认情况下,一个NSOperation开始执行之后,会一直执行任务到结束,就比如上面的DownloadOperation,默认会执行完main方法中的所有代码。
* NSOperation提供了一个cancel方法,可以取消当前的操作。
* 如果是自定义NSOperation的话,需要手动处理这个取消事件。比如,一旦调用了cancel方法,应该马上终止main方法的执行,并及时回收一些资源。
* 处理取消事件的具体做法是:在main方法中定期地调用isCancelled方法检测操作是否已经被取消,也就是说是否调用了cancel方法,如果返回YES,表示已取消,则立即让main方法返回。
* 以下地方可能需要调用isCancelled方法:
- 在执行任何实际的工作之前,也就是在main方法的开头。因为取消可能发生在任何时候,甚至在operation执行之前。
- 执行了一段耗时的操作之后也需要检测操作是否已经被取消
![](/assets/blank.gif)
1 - (void)main {2 // 新建一个自动释放池,如果是异步执行操作,那么将无法访问到主线程的自动释放池3 @autoreleasepool {4 if (self.isCancelled) return;5 6 // 获取图片数据7 NSURL *url = [NSURL URLWithString:self.imageUrl];8 NSData *imageData = [NSData dataWithContentsOfURL:url];9 10 if (self.isCancelled) { 11 url = nil; 12 imageData = nil; 13 return; 14 } 15 16 // 初始化图片 17 UIImage *image = [UIImage imageWithData:imageData]; 18 19 if (self.isCancelled) { 20 image = nil; 21 return; 22 } 23 24 if ([self.delegate respondsToSelector:@selector(downloadFinishWithImage:)]) { 25 // 把图片数据传回到主线程 26 [(NSObject *)self.delegate performSelectorOnMainThread:@selector(downloadFinishWithImage:) withObject:image waitUntilDone:NO]; 27 } 28 } 29 }
![](/assets/blank.gif)
* 在第4行main方法的开头就先判断operation有没有被取消。如果被取消了,那就没有必要往下执行了
* 经过第8行下载图片后,在第10行也需要判断操作有没有被取消
* 总之,执行了一段比较耗时的操作之后,都需要判断操作有没有被取消
* 图片下载完毕后,在第26行将图片数据传递给了代理(delegate)对象
转载于:https://www.cnblogs.com/pb89/p/4708822.html
多线程编程2-NSOperation相关推荐
- 多线程编程2 - NSOperation
一.NSOperation 1.简介 NSOperation实例封装了需要执行的操作和执行操作所需的数据,并且能够以并发或非并发的方式执行这个操作. NSOperation本身是抽象基类,因此必须使用 ...
- iOS多线程编程技术之NSThread、Cocoa NSOperation、GCD
iOS有三种多线程编程的技术,分别是:NSThread .Cocoa NSOperation .GCD. 这三种编程方式从上到下,抽象度层次是从低到高的,抽象度越高的使用越简单,也是Apple最推荐使 ...
- 【转】iOS多线程编程技术之NSThread、Cocoa NSOperation、GCD
转自容芳志的博客 简介 iOS有三种多线程编程的技术,分别是: (一)NSThread (二)Cocoa NSOperation (三)GCD(全称:Grand Central Dispatch) ...
- 多线程编程1-NSThread
前言 每个iOS应用程序都有个专门用来更新显示UI界面.处理用户触摸事件的主线程,因此不能将其他太耗时的操作放在主线程中执行,不然会造成主线程堵塞(出现卡机现象),带来极坏的用户体验.一般的解决方案就 ...
- iOS多线程:『NSOperation、NSOperationQueue』详尽总结
2019独角兽企业重金招聘Python工程师标准>>> iOS多线程:『NSOperation.NSOperationQueue』详尽总结 转载: 原地址https://www.ji ...
- 多线程编程1 - NSThread
每个iOS应用程序都有个专门用来更新显示UI界面.处理用户的触摸事件的主线程,因此不能将其他太耗时的操作放在主线程中执行,不然会造成主线程堵塞(出现卡机现象),带来极坏的用户体验.一般的解决方案就是将 ...
- 多线程编程 (1) -NSThread
多线程编程 (1) -NSThread 每个iOS应用程序都有个专门用来更新显示UI界面.处理用户触摸事件的主线程,因此不能将其他太耗时的操作放在主线程中执行,不然会造成主线程堵塞(出现卡机现象),带 ...
- iOS开发-多线程编程技术(Thread、Cocoa operations、GCD)
简介 在软件开发中,多线程编程技术被广泛应用,相信多线程任务对我们来说已经不再陌生了.有了多线程技术,我们可以同做多个事情,而不是一个一个任务地进行.比如:前端和后台作交互.大任务(需要耗费一定的时间 ...
- iOS多线程编程的知识梳理
多线程编程也称之为并发编程,由于其作用大,有比较多的理论知识,因此在面试中也是受到面试官的青睐.在日常项目开发中,至少网络请求上是需要使用到多线程知识的,虽然使用第三方的框架比如AFNetworkin ...
最新文章
- http://blog.csdn.net/fanzhonglei
- MongoDB入门学习(二):MongoDB的基本概念和数据类型
- Java编程入门(2.1):基础Java应用程序
- MATLAB读取excel文件中的数据
- Python正则表达式如何进行字符串替换实例
- 软件工程之快速原型模型
- 同一条sql insert 有时快有时慢 引发的血案
- python local_Python3中 对local和nonlocal 关键字的改善认识(新手向)
- python使用的一些小事儿
- java判断两个日期相差天数
- Qt C++ QSerialPortInfo的测试
- 机器学习基础:评价指标(Machine Learning Fundamentals: Evaluation Metrics)
- Atitit.基于时间戳的农历日历历法日期计算
- Ubuntu 20.04桌面美化
- 20210706 为什么edge无法修改主页?或者启动后两个页面?
- Codeforces 1292C Xenon's Attack on the Gangs
- Bond随你入坑kubernetes之:入门篇 - 搭建多节点集群+Dashboard+监控
- 论文笔记:ReDet: A Rotation-equivariant Detector for Aerial Object Detection
- 如何判断合法的立即数
- 编译后的hue 替换cdh默认版本hue步骤
热门文章
- 郑洁又淘汰了一个美女瓦伊迪索娃
- VC,一条会被鼠标移动的直线
- php 命令行 php.ini,php web环境和命令行环境下查找php.ini的方法分享
- python定义一个人类_Python类的定义、继承及类对象使用方法简明教程
- 华为 原生android 6.0,安卓6.0原生桌面轻体验,我的手机从未如此流畅过
- html select选择事件_按键精灵的Html系列命令实战讲解
- python中的类装饰器应用场景_Python 自定义装饰器使用写法及示例代码
- mysql 上一条_mysql选择上一条、下一条数据记录
- android+建模工具,什么是适用于Android Studio的3D模型环境的最佳工具
- css html 抽屉,CSS快速入门-前端布局1(抽屉)(示例代码)