iOS实现多线程的方式有三种,分别是NSThreadNSOperationGCD

关于GCD,请阅读GCD深入浅出学习


NSOperation封装了需要执行的操作和执行操作所需的数据,提供了并发或非并发操作,可以设置最大并发数,取消操作等。

iOS使用NSOperation的方式有两种: * 直接使用系统提供的两个子类:NSInvocationOperationNSBlockOperation * 继承于NSOperation

这里所说的抽象类不是真正的抽象类,不像C++那种纯虚函数,不能实例化。在Ojbective-C中是没有纯虚函数的,因此它是可以实例化的。只是由于没有提供任务接口,因此实例化了也没有意义。

 
1
2
3
 
;
 

 

注意:我们不能直接使用NSOperation这个类,这个类相当于一个抽象类,不能直接实例化,必须重写main方法。

NSOperation基类API


下面简单说明NSOperation所提供的一些操作。

1.执行任务


NSOperation提供了start方法开启任务执行操作,NSOperation对象默认按同步方式执行,也就是在调用start方法的那个线程中直接执行。

 
1
2
3
4
5
 
{
;
}
 

 

2.判断是否是同步还是异步


NSOperation提供的isConcurrent可判断是同步还是异步执行。isConcurrent默认值为NO,表示操作与调用线程同步执行。不过这个方法在7.0之后就被废弃了,改成使用isAsynchronous判断了。

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 
{
{
;
{
;
}
{
{
;
{
;
}
}
 

 

3.判断任务是否在执行中


NSOperation提供了isExecuting,可判断任务是否正在执行中。

 
1
2
3
4
5
 
{
;
}
 

 

4.判断任务是否已经准备好


NSOperation提供了isReady方法来获取任务是否已经为执行准备好。

 
1
2
3
4
5
 
{
;
}
 

 

5.判断任务已经已完成


NSOperation提供了isFinished,可判断任务是否已经执行完成。

 
1
2
3
4
5
 
{
;
}
 

 

6.取消任务/判断任务状态

NSOperation提供了isCancelled,可判断任务是否已经执行完成,而要取消任务,可调用cancel方法。

 
1
2
3
4
5
 
{
;
}
 

 

7.任务完成回调


如果我们想在一个NSOperation执行完毕后做一些事情,可以调用NSOperationcompletionBlock属性来设置在任务完成以后我们还想做的事情。

我们可以通过这种点语法设置:

 
1
2
3
4
5
 
{
;
;
 

也可以通过中括号方式设置:

 
1
2
3
4
5
 
{
;
;
 

 

8.任务优先级


如下,NSOperation为我们提供了在NSOperationQueue调度队列中任务的优先级设置。

 
1
2
3
4
5
6
7
8
9
10
11
 
{
,
,
,
,
8
;
 
;
 

 

NSInvocationOperation子类


NSInvocationOperation是继承于NSOperation,提供创建任务的方式是通过selector

 
1
2
3
4
 
;
;
 

对于第二个初始化方法已经被废弃了,第二个初始化方法是通过运行时的方式来添加任务的,操作起来比较复杂。第一种就是很普通的方式,是很常见的target-action设计模式。

 
1
2
3
4
5
 
;
// 开始执行任务(同步执行)
;
 

调用start方法是同步执行的。如果要异步执行,可以放到NSOperationQueue队列中,它就相当于一个线程池,而且任务一旦放进去,就会按照FIFO的原则严格执行任务。任务放到线程池中后,是否会马上执行,是根据当前所设置的并发数量决定的。

看看我们下载一个图片:

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 
{
:self
)
;
  
;
;
}
 
{
;
;
;
  
{
;
;
}
 

我们需要注意,最后在更新UI的时候,一定要回到主线程,否则UI效果不会马上变化。当然,我们也可以使用别的方式回到主线程更新UI

 
1
2
3
4
5
6
7
 
  
 
  
  
  
 

 

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
 
{
,
,
,
;
;
 
;
{
,
,
,
;
;
 
{
,
,
,
;
;
 
{
,
,
,
;
;
 
// 开始执行任务
]
 

看看打印结果:

 
1
2
3
4
5
6
 
0
0
0
0
 

由此,我们可以看到第一个任务在主线程执行,第二、三、四个任务都是其它子线程完成的。这四个任务都是同步执行的。其中的number代表线程的id

当我们把[operation start]这行改成这样:

 
1
2
3
4
5
 
;
;
;
 

其打印结果如下:

 
1
2
3
4
5
6
 
0
0
0
0
 

由于我们设置了最大并发数量为2,因此同时能执行的任务数量最多两个。而这四个任务都不是在主线程执行的,全部放到子线程中执行了。我们发现isAync都为0,也就是说operationisAsynchronous方法返回都是NO

注意:并发与异步不是同一个概念

要异步执行,可以这样:

 
1
2
3
4
5
6
7
8
9
 
{
{
,
,
;
;
;
 

 

自定义NSOperation


如果NSInvocationOperationNSBlockOperation对象不能满足需求, 我们可以直接继承NSOperation, 并添加额外的功能。继承所需的工作量主要取决于你要实现非并发还是并发的NSOperation。定义非并发的NSOperation要简单许多,只需要重载-main这个方法,在这个方法里面执行主任务,并正确地响应取消事件; 对于并发NSOperation, 必须重写NSOperation的多个基本方法进行实现。

非并发自定义NSOperation

我们定义一个图片下载类来说明如何自定义非并发的NSOperation

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 
;
/*!
*  @author 黄仪标, 15-11-24 22:11:50
*
*  下载operation
*/
: NSOperation
 
;
;
 
;
 
@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.
//
 
 
DownloadOperation
 
{
{
;
;
}
  
;
}
 
// 必须重写这个主方法
{
// 新建一个自动释放池,如果是异步执行操作,那么将无法访问到主线程的自动释放池
{
// 如果刚进来,就已经被取消了,则直接退出
{
;
}
    
// 获取图片数据
;
;
    
// 被取消,也有可能发生在获取数据后
{
;
;
;
}
    
;
    
// 被取消,也可能发生在转换的地方
{
;
;
}
    
{
{
;
;
}
}
}
 
@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
 
{
;
;
;
{
;
;
;
{
;
;
;
{
;
;
;
{
;
;
;
 
;
;
;
;
;
;
;
 

效果如下:

 标哥的技术博客

http://www.henishuo.com/ios-nsoperation-queue/

相关文章: