iOS开发多线程--(NSOperation/Queue)
iOS
实现多线程的方式有三种,分别是NSThread
、NSOperation
、GCD
。
关于GCD
,请阅读GCD深入浅出学习
简介
NSOperation
封装了需要执行的操作和执行操作所需的数据,提供了并发或非并发操作,可以设置最大并发数,取消操作等。
iOS
使用NSOperation
的方式有两种: * 直接使用系统提供的两个子类:NSInvocationOperation
和NSBlockOperation
* 继承于NSOperation
这里所说的抽象类不是真正的抽象类,不像C++
那种纯虚函数,不能实例化。在Ojbective-C
中是没有纯虚函数的,因此它是可以实例化的。只是由于没有提供任务接口,因此实例化了也没有意义。
1
2
3
|
NSOperation *op = [[NSOperation alloc] init];
|
注意:我们不能直接使用
NSOperation
这个类,这个类相当于一个抽象类,不能直接实例化,必须重写main
方法。
NSOperation
基类API
下面简单说明NSOperation
所提供的一些操作。
1.执行任务
NSOperation
提供了start
方法开启任务执行操作,NSOperation
对象默认按同步方式执行,也就是在调用start
方法的那个线程中直接执行。
1
2
3
4
5
|
if (!operation.isExecuting) {
[operation start];
}
|
2.判断是否是同步还是异步
NSOperation
提供的isConcurrent
可判断是同步还是异步执行。isConcurrent
默认值为NO
,表示操作与调用线程同步执行。不过这个方法在7.0
之后就被废弃了,改成使用isAsynchronous
判断了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
if ([UIDevice currentDevice].systemVersion.intValue >= 7.0) {
if (operation.isAsynchronous) {
NSLog(@"异步");
} else {
NSLog(@"同步");
}
} else {
if (operation.isConcurrent) {
NSLog(@"异步");
} else {
NSLog(@"同步");
}
}
|
3.判断任务是否在执行中
NSOperation
提供了isExecuting
,可判断任务是否正在执行中。
1
2
3
4
5
|
if (!operation.isExecuting) {
[operation start];
}
|
4.判断任务是否已经准备好
NSOperation
提供了isReady
方法来获取任务是否已经为执行准备好。
1
2
3
4
5
|
if (!operation.isReady) {
[operation start];
}
|
5.判断任务已经已完成
NSOperation
提供了isFinished
,可判断任务是否已经执行完成。
1
2
3
4
5
|
if (operation.isFinished) {
NSLog(@"finished");
}
|
6.取消任务/判断任务状态
NSOperation
提供了isCancelled
,可判断任务是否已经执行完成,而要取消任务,可调用cancel
方法。
1
2
3
4
5
|
if (!operation.isCancelled) {
[operation cancel];
}
|
7.任务完成回调
如果我们想在一个NSOperation
执行完毕后做一些事情,可以调用NSOperation
的completionBlock
属性来设置在任务完成以后我们还想做的事情。
我们可以通过这种点语法设置:
1
2
3
4
5
|
operation.completionBlock = ^() {
NSLog(@"任务执行完毕");
};
|
也可以通过中括号方式设置:
1
2
3
4
5
|
[operation setCompletionBlock:^{
NSLog(@"任务执行完毕");
}];
|
8.任务优先级
如下,NSOperation
为我们提供了在NSOperationQueue
调度队列中任务的优先级设置。
1
2
3
4
5
6
7
8
9
10
11
|
typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
NSOperationQueuePriorityVeryLow = -8L,
NSOperationQueuePriorityLow = -4L,
NSOperationQueuePriorityNormal = 0,
NSOperationQueuePriorityHigh = 4,
NSOperationQueuePriorityVeryHigh = 8
};
@property NSOperationQueuePriority queuePriority;
|
NSInvocationOperation
子类
NSInvocationOperation
是继承于NSOperation
,提供创建任务的方式是通过selector
。
1
2
3
4
|
- (nullable instancetype)initWithTarget:(id)target selector:(SEL)sel object:(nullable id)arg;
- (instancetype)initWithInvocation:(NSInvocation *)inv NS_DESIGNATED_INITIALIZER;
|
对于第二个初始化方法已经被废弃了,第二个初始化方法是通过运行时的方式来添加任务的,操作起来比较复杂。第一种就是很普通的方式,是很常见的target-action
设计模式。
1
2
3
4
5
|
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(updateUI) object:nil];
// 开始执行任务(同步执行)
[operation start];
|
调用start
方法是同步执行的。如果要异步执行,可以放到NSOperationQueue
队列中,它就相当于一个线程池,而且任务一旦放进去,就会按照FIFO
的原则严格执行任务。任务放到线程池中后,是否会马上执行,是根据当前所设置的并发数量决定的。
看看我们下载一个图片:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
- (void)test1 {
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self
selector:@selector(downloadImage:)
object:@"图片的URL"];
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
[queue addOperation:operation];
}
- (void)downloadImage:(NSString *)url {
NSURL *nsUrl = [NSURL URLWithString:url];
NSData *data = [[NSData alloc] initWithContentsOfURL:nsUrl];
UIImage *image = [[UIImage alloc] initWithData:data];
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = image;
});
}
|
我们需要注意,最后在更新UI
的时候,一定要回到主线程,否则UI
效果不会马上变化。当然,我们也可以使用别的方式回到主线程更新UI
:
1
2
3
4
5
6
7
|
[self performSelectorOnMainThread:@selector(updateUI:) withObject:image waitUntilDone:YES];
- (void)updateUI:(UIImage *) image{
self.imageView.image = image;
}
|
NSBlockOperation
子类
NSBlockOperation
是直接继承于NSOperation
的子类,它能够并发地执行一个或多个block
对象,所有的block
都执行完之后,操作才算真正完成。
添加任务
NSBlockOperation
都是block
任务,操作起来比较简洁一些。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^(){
NSLog(@"这是第一个任务在线程:%@执行,isMainThread: %d,isAync: %d",
[NSThread currentThread],
[NSThread isMainThread],
[operation isAsynchronous]);
}];
__weak typeof(operation) weakOperation = operation;
[operation addExecutionBlock:^() {
NSLog(@"这是第二个任务在线程:%@执行,isMainThread: %d,isAync: %d",
[NSThread currentThread],
[NSThread isMainThread],
[weakOperation isAsynchronous]);
}];
[operation addExecutionBlock:^() {
NSLog(@"这是第三个任务在线程:%@执行,isMainThread: %d,isAync: %d",
[NSThread currentThread],
[NSThread isMainThread],
[weakOperation isAsynchronous]);
}];
[operation addExecutionBlock:^() {
NSLog(@"这是第四个任务在线程:%@执行,isMainThread: %d,isAync: %d",
[NSThread currentThread],
[NSThread isMainThread],
[weakOperation isAsynchronous]);
}];
// 开始执行任务
[operation start]
|
看看打印结果:
1
2
3
4
5
6
|
2015-11-24 17:15:49.489 TestGCD[42401:3307632] 这是第一个任务在线程:<NSThread: 0x7fb369e02c30>{number = 1, name = main}执行,isMainThread: 1,isAync: 0
2015-11-24 17:15:49.489 TestGCD[42401:3307797] 这是第二个任务在线程:<NSThread: 0x7fb369ca8880>{number = 2, name = (null)}执行,isMainThread: 0,isAync: 0
2015-11-24 17:15:49.489 TestGCD[42401:3307809] 这是第三个任务在线程:<NSThread: 0x7fb369ca92b0>{number = 3, name = (null)}执行,isMainThread: 0,isAync: 0
2015-11-24 17:15:49.489 TestGCD[42401:3307798] 这是第四个任务在线程:<NSThread: 0x7fb369f2acd0>{number = 4, name = (null)}执行,isMainThread: 0,isAync: 0
|
由此,我们可以看到第一个任务在主线程执行,第二、三、四个任务都是其它子线程完成的。这四个任务都是同步执行的。其中的number
代表线程的id
。
当我们把[operation start]
这行改成这样:
1
2
3
4
5
|
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:operation];
[queue setMaxConcurrentOperationCount:2];
|
其打印结果如下:
1
2
3
4
5
6
|
2015-11-24 17:22:26.206 TestGCD[42558:3316054] 这是第三个任务在线程:<NSThread: 0x7f9000744750>{number = 4, name = (null)}执行,isMainThread: 0,isAync: 0
2015-11-24 17:22:26.206 TestGCD[42558:3316053] 这是第一个任务在线程:<NSThread: 0x7f900061b6f0>{number = 2, name = (null)}执行,isMainThread: 0,isAync: 0
2015-11-24 17:22:26.206 TestGCD[42558:3316055] 这是第二个任务在线程:<NSThread: 0x7f90006183b0>{number = 3, name = (null)}执行,isMainThread: 0,isAync: 0
2015-11-24 17:22:26.206 TestGCD[42558:3316062] 这是第四个任务在线程:<NSThread: 0x7f9000609680>{number = 5, name = (null)}执行,isMainThread: 0,isAync: 0
|
由于我们设置了最大并发数量为2,因此同时能执行的任务数量最多两个。而这四个任务都不是在主线程执行的,全部放到子线程中执行了。我们发现isAync都为0,也就是说operation
的isAsynchronous
方法返回都是NO
。
注意:并发与异步不是同一个概念
要异步执行,可以这样:
1
2
3
4
5
6
7
8
9
|
[operation addExecutionBlock:^() {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"这是第四个任务在线程:%@执行,isMainThread: %d",
[NSThread currentThread],
[NSThread isMainThread]);
});
}];
|
自定义NSOperation
如果NSInvocationOperation
和NSBlockOperation
对象不能满足需求, 我们可以直接继承NSOperation
, 并添加额外的功能。继承所需的工作量主要取决于你要实现非并发还是并发的NSOperation
。定义非并发的NSOperation
要简单许多,只需要重载-main
这个方法,在这个方法里面执行主任务,并正确地响应取消事件; 对于并发NSOperation
, 必须重写NSOperation
的多个基本方法进行实现。
非并发自定义NSOperation
我们定义一个图片下载类来说明如何自定义非并发的NSOperation
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
typedef void(^HYBDownloadReponse)(UIImage *image);
/*!
* @author 黄仪标, 15-11-24 22:11:50
*
* 下载operation
*/
@interface DownloadOperation : NSOperation
@property (nonatomic, copy) NSString *url;
@property (nonatomic, copy) HYBDownloadReponse responseBlock;
- (instancetype)initWithUrl:(NSString *)url completion:(HYBDownloadReponse)completion;
@end
|
下面看看实现方法怎么实现的,关键点在于-main
方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
|
//
// DownloadOperation.m
// TestGCD
//
// Created by huangyibiao on 15/11/24.
// Copyright © 2015年 huangyibiao. All rights reserved.
//
#import "DownloadOperation.h"
@implementation DownloadOperation
- (instancetype)initWithUrl:(NSString *)url completion:(HYBDownloadReponse)completion {
if (self = [super init]) {
self.url = url;
self.responseBlock = completion;
}
return self;
}
// 必须重写这个主方法
- (void)main {
// 新建一个自动释放池,如果是异步执行操作,那么将无法访问到主线程的自动释放池
@autoreleasepool {
// 如果刚进来,就已经被取消了,则直接退出
if (self.isCancelled) {
return;
}
// 获取图片数据
NSURL *url = [NSURL URLWithString:self.url];
NSData *imageData = [NSData dataWithContentsOfURL:url];
// 被取消,也有可能发生在获取数据后
if (self.isCancelled) {
url = nil;
imageData = nil;
return;
}
UIImage *image = [UIImage imageWithData:imageData];
// 被取消,也可能发生在转换的地方
if (self.isCancelled) {
image = nil;
return;
}
if (self.responseBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
self.responseBlock(image);
});
}
}
}
@end
|
我们测试一下并发:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
DownloadOperation *operation = [[DownloadOperation alloc] initWithUrl:@"https://mmbiz.qlogo.cn/mmbiz/sia5QxFVcFD0wkCgnmf6DVxI6fVewNS8rhtZb71v2DMpDy8jIdtviaetzicwQzTEoKKyHAN96Beibk2G61tZpezQ0Q/0?wx_fmt=png" completion:^(UIImage *image) {
self.imageView.image = image;
NSLog(@"fisrt");
}];
DownloadOperation *operation1 = [[DownloadOperation alloc] initWithUrl:@"https://mmbiz.qlogo.cn/mmbiz/sia5QxFVcFD0wkCgnmf6DVxI6fVewNS8rhtZb71v2DMpDy8jIdtviaetzicwQzTEoKKyHAN96Beibk2G61tZpezQ0Q/0?wx_fmt=png" completion:^(UIImage *image) {
self.imageView1.image = image;
NSLog(@"second");
}];
DownloadOperation *operation2 = [[DownloadOperation alloc] initWithUrl:@"https://mmbiz.qlogo.cn/mmbiz/sia5QxFVcFD0wkCgnmf6DVxI6fVewNS8rhtZb71v2DMpDy8jIdtviaetzicwQzTEoKKyHAN96Beibk2G61tZpezQ0Q/0?wx_fmt=png" completion:^(UIImage *image) {
self.imageView2.image = image;
NSLog(@"third");
}];
DownloadOperation *operation3 = [[DownloadOperation alloc] initWithUrl:@"https://mmbiz.qlogo.cn/mmbiz/sia5QxFVcFD0wkCgnmf6DVxI6fVewNS8rhtZb71v2DMpDy8jIdtviaetzicwQzTEoKKyHAN96Beibk2G61tZpezQ0Q/0?wx_fmt=png" completion:^(UIImage *image) {
self.imageView3.image = image;
NSLog(@"fourth");
}];
DownloadOperation *operation4 = [[DownloadOperation alloc] initWithUrl:@"https://mmbiz.qlogo.cn/mmbiz/sia5QxFVcFD0wkCgnmf6DVxI6fVewNS8rhtZb71v2DMpDy8jIdtviaetzicwQzTEoKKyHAN96Beibk2G61tZpezQ0Q/0?wx_fmt=png" completion:^(UIImage *image) {
self.imageView4.image = image;
NSLog(@"fifth");
}];
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
[queue addOperation:operation];
[queue addOperation:operation1];
[queue addOperation:operation2];
[queue addOperation:operation3];
[queue addOperation:operation4];
[queue setMaxConcurrentOperationCount:2];
|
效果如下:
标哥的技术博客
http://www.henishuo.com/ios-nsoperation-queue/
iOS开发多线程--(NSOperation/Queue)的更多相关文章
- iOS 开发多线程 —— NSOperation
本文是根据文顶顶老师的博客学习而来,转载地址:http://www.cnblogs.com/wendingding/p/3809042.html 一.NSOperation简介 1.简单说明 NSOp ...
- iOS开发-多线程NSOperation和NSOperationQueue
上一篇文章稍微提及了一下NSThread的使用,NSThread能直观地控制线程对象,不过需要自己管理线程的生命周期,线程同步,用起来比较繁琐,而且比较容易出错.不过Apple给出了自己的解决方案NS ...
- iOS开发多线程篇—NSOperation简单介绍
iOS开发多线程篇—NSOperation简单介绍 一.NSOperation简介 1.简单说明 NSOperation的作⽤:配合使用NSOperation和NSOperationQueue也能实现 ...
- iOS开发多线程篇—NSOperation基本操作
iOS开发多线程篇—NSOperation基本操作 一.并发数 (1)并发数:同时执⾏行的任务数.比如,同时开3个线程执行3个任务,并发数就是3 (2)最大并发数:同一时间最多只能执行的任务的个数. ...
- iOS开发多线程篇—自定义NSOperation
iOS开发多线程篇—自定义NSOperation 一.实现一个简单的tableView显示效果 实现效果展示: 代码示例(使用以前在主控制器中进行业务处理的方式) 1.新建一个项目,让控制器继承自UI ...
- iOS开发多线程篇 09 —NSOperation简单介绍
iOS开发多线程篇—NSOperation简单介绍 一.NSOperation简介 1.简单说明 NSOperation的作⽤:配合使用NSOperation和NSOperationQueue也能实现 ...
- iOS开发多线程篇 11 —自定义NSOperation
iOS开发多线程篇—自定义NSOperation 一.实现一个简单的tableView显示效果 实现效果展示: 代码示例(使用以前在主控制器中进行业务处理的方式) 1.新建一个项目,让控制器继承自UI ...
- iOS开发多线程篇 10 —NSOperation基本操作
iOS开发多线程篇—NSOperation基本操作 一.并发数 (1)并发数:同时执⾏行的任务数.比如,同时开3个线程执行3个任务,并发数就是3 (2)最大并发数:同一时间最多只能执行的任务的个数. ...
- 【iOS开发】NSOperation简单介绍
iOS开发多线程篇—NSOperation简单介绍 一.NSOperation简介 1.简单说明 NSOperation的作⽤:配合使用NSOperation和NSOperationQueue也能实现 ...
- iOS 开发多线程篇—GCD的常见用法
iOS开发多线程篇—GCD的常见用法 一.延迟执行 1.介绍 iOS常见的延时执行有2种方式 (1)调用NSObject的方法 [self performSelector:@selector(run) ...
随机推荐
- PHP笔记-PHP中Web Service.
这几天工作需要.net站点免登陆访问PHP的Wiki站点. PHP不熟,感觉很苦逼.任务下来了,必须搞定.准备用SSO,太麻烦了,要改写别人很多代码,这个是第三方CMS,封装的很厉害,不好改.最后我的 ...
- 利用python scrapy 框架抓取豆瓣小组数据
因为最近在找房子在豆瓣小组-上海租房上找,发现搜索困难,于是想利用爬虫将数据抓取. 顺便熟悉一下Python. 这边有scrapy 入门教程出处:http://www.cnblogs.com/txw1 ...
- 在本地环境用虚拟机win2008 sever搭建VS2013 + SVN 代码版本控制环境
此文仅仅是自己笔记做个备忘.因为自己开发一些中小型的软件经常需要修修改改,特别是winform界面的大改动.经常需要对版本进行管理.而租用分布式服务器和远程服务器都不是自己想要的.本文结合虚拟机 + ...
- 软件工程随堂小作业——随机四则运算(C++)
一.设计思路: 1.程序的主体部分是循环输出,次数即题目数目由用户输入: 2.三个整型变量+rand函数来实现随机数四则运算,一个变量代表加减乘除,另外两个用作运算数: 3.用户体验:题目分三列,排列 ...
- 嵌入字体@font-face
嵌入字体@font-face @font-face能够加载服务器端的字体文件,让浏览器端可以显示用户电脑里没有安装的字体. 语法: @font-face { font-family : 字体名称; s ...
- boost之function
boost中function是对函数指针和函数对象的进行封装的模板类. 定义示例:function<int()> func生成一个空的对象,表示函数参数个数为零,返回类型为int. #in ...
- 转载:百度原CTO李一男经典语录
原文地址:http://www.cnblogs.com/marvin/archive/2010/01/20/1652088.html 百度原CTO李一男经典语录 [1]好好规划自己的路,不要跟着感觉走 ...
- Codeforces 343D Water Tree 分类: Brush Mode 2014-10-05 14:38 98人阅读 评论(0) 收藏
Mad scientist Mike has constructed a rooted tree, which consists of n vertices. Each vertex is a res ...
- API文档管理工具-数据库表结构思考.
API文档管理工具-数据库表结构思考. PS: 管理工具只是为了方便自己记录API的一些基本信息,方便不同的开发人员 (App Developer, Restful API Developer)之间的 ...
- .NET设计模式(4):建造者模式(Builder Pattern)(转)
概述 在软件系统中,有时候面临着“一个复杂对象”的创建工作,其通常由各个部分的子对象用一定的算法构成:由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法确相对稳定. ...