基本内容

本节主要介绍iOS中多线程相关的内容,先简单介绍NSOperation的使用,然后结合GCD实现任务之间的管理,比如每个任务完成之后的处理以及任务与任务之间的依赖,所有任务完成之后的处理等,通过AFNetworking源码分析其具体实现。

NSoperation

通过NSOpeartion去完成某个任务的时候,有3种方式:

  • NSInvocationOperation
  • NSBlockOperation
  • 自己继承NSOperation实现具体的任务操作

NSInvocationOperation

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
- (void)viewDidLoad {

[super viewDidLoad];

// Do any additional setup after loading the view, typically from a nib.
NSOperationQueue *queue = [NSOperationQueue new];

NSInvocationOperation *ope = [[NSInvocationOperation alloc] initWithTarget:
self selector:@selector(invocationOperationCall1) object:nil];

NSInvocationOperation *ope2 = [[NSInvocationOperation alloc] initWithTarget
:self selector:@selector(invocationOperationCall2) object:nil];

[queue addOperation:ope];

[queue addOperation:ope2];

}

-(void)invocationOperationCall1{

NSLog(@"%@", NSStringFromSelector(_cmd));

NSLog(@"%@", [NSThread currentThread]);

}

-(void)invocationOperationCall2{

NSLog(@"%@", NSStringFromSelector(_cmd));

NSLog(@"%@", [NSThread currentThread]);

}

输出:

1
2
3
4
5
6
7
2016-01-13 14:23:54.791 operation+dispatch_group[6032:2162368]
invocationOperationCall1------<NSThread: 0x7fb6ad8019a0>{number = 3, name = (
null)}

2016-01-13 14:23:54.791 operation+dispatch_group[6032:2162371]
invocationOperationCall2------<NSThread: 0x7fb6adaec8f0>{number = 2, name = (
null)}

从日志中看出任务分别在不同的线程中执行

1
2
3
4
5
NSInvocationOperation *ope = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationOperationCall1) object:nil];
NSInvocationOperation *ope2 = [[NSInvocationOperation alloc] initWithTarget:
self selector:@selector(invocationOperationCall2) object:nil];
[ope start];
[ope2 start];

输出:

1
2
3
4
5
6
7
2016-01-13 14:27:38.226 operation+dispatch_group[6053:2164444] 
invocationOperationCall1------<NSThread: 0x7fae69508370>{number = 1, name =
main}

2016-01-13 14:27:38.226 operation+dispatch_group[6053:2164444]
invocationOperationCall2------<NSThread: 0x7fae69508370>{number = 1, name =
main}

从日志中看出,如果不添加到queue中,则任务是同步在当前线程中执行的。

还可以添加NSOperation对象之间的依赖,让每个任务之间按一定的顺序执行

1
2
3
4
5
6
7
8
9
10
11
12
13
NSOperationQueue *queue = [NSOperationQueue new];

NSInvocationOperation *ope = [[NSInvocationOperation alloc] initWithTarget:self
selector:@selector(invocationOperationCall1) object:nil];

NSInvocationOperation *ope2 = [[NSInvocationOperation alloc] initWithTarget:
self selector:@selector(invocationOperationCall2) object:nil];

[ope addDependency:ope2];

[queue addOperation:ope];

[queue addOperation:ope2];

输出:

1
2
3
4
5
6
7
2016-01-13 19:27:49.182 operation+dispatch_group[13865:2284382] 
invocationOperationCall2------<NSThread: 0x7fa26300ff40>{number = 2, name = (
null)}

2016-01-13 19:27:49.183 operation+dispatch_group[13865:2284382]
invocationOperationCall1------<NSThread: 0x7fa26300ff40>{number = 2, name = (
null)}

虽然ope和ope2之间是并行的,但是添加依赖之后ope会等到ope2执行完毕之后才开始执行。

NSBlockOperation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
NSBlockOperation *ope = [NSBlockOperation blockOperationWithBlock:^{

NSLog(@"task A, %@", [NSThread currentThread]);

}];

[ope addExecutionBlock:^{

NSLog(@"task B, %@", [NSThread currentThread]);

}];

[ope addExecutionBlock:^{

NSLog(@"task C, %@", [NSThread currentThread]);

}];

[ope start];

输出:

1
2
3
4
5
6
7
8
9

2016-01-13 14:17:36.594 operation+dispatch_group[5990:2158464]
task A, <NSThread: 0x7ff1217073d0>{number = 1, name = main}

2016-01-13 14:17:36.594 operation+dispatch_group[5990:2158531]
task B, <NSThread: 0x7ff1215029f0>{number = 2, name = (null)}

2016-01-13 14:17:36.594 operation+dispatch_group[5990:2158532]
task C, <NSThread: 0x7ff1217a6af0>{number = 3, name = (null)}

自定义NSOperation(结合AFNetworking源码)

AFNetworking源代码:

AFURLConnectionOperation.m

1
2
@interface AFURLConnectionOperation : NSOperation <NSURLConnectionDelegate, 
NSURLConnectionDataDelegate, NSSecureCoding, NSCopying>

AFURLConnectionOperation是继承自NSOperation

AFURLConnectionOperation.m

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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83

- (void)start {

[self.lock lock];

if ([self isCancelled]) {
[self performSelector:@selector(cancelConnection) onThread:[[self class
] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.
runLoopModes allObjects]];

} else if ([self isReady]) {
self.state = AFOperationExecutingState;
//使用AFNetworking网络线程去调用OperationDidStart函数
[self performSelector:@selector(operationDidStart) onThread:[[self
class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[
self.runLoopModes allObjects]];
}

[self.lock unlock];
}
//类方法 获取网络线程 全局唯一的
+ (NSThread *)networkRequestThread {

static NSThread *_networkRequestThread = nil;

static dispatch_once_t oncePredicate;

dispatch_once(&oncePredicate, ^{

_networkRequestThread = [[NSThread alloc] initWithTarget:self selector:
@selector(networkRequestThreadEntryPoint:) object:nil];

[_networkRequestThread start];

});
return _networkRequestThread;

}

//初始化网络线程
+ (void)networkRequestThreadEntryPoint:(id)__unused object {

@autoreleasepool {

[[NSThread currentThread] setName:@"AFNetworking"];
//新建网络线程的runloop对象
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
//保证网络线程在没有任何port消息的时候一直处于活跃状态,防止进入休眠,随时处理网络请求或者数据返回。
[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
//开始运行网络线程的runloop
[runLoop run];

}
}
- (void)operationDidStart {

[self.lock lock];

if (![self isCancelled]) {

self.connection = [[NSURLConnection alloc] initWithRequest:self.request
delegate:self startImmediately:NO];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];

for (NSString *runLoopMode in self.runLoopModes) {
//将网络请求相关的事件源添加到网络线程的runloop中,网络回调和数据返回都依赖于网络线程runloop去监听
[self.connection scheduleInRunLoop:runLoop forMode:runLoopMode];
//将流相关的事件源添加到网络线程的runloop中,流的读写都依赖于网络线程runloop去监听
[self.outputStream scheduleInRunLoop:runLoop forMode:runLoopMode];
}
[self.outputStream open];
//开始网络请求
[self.connection start];

}
[self.lock unlock];

//让主线程去触发”已经开始网络请求“这一事件。
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:
AFNetworkingOperationDidStartNotification object:self];
});
}

AFURLConnectionOperation自己重写了NSOperation中的start方法,具体指定了需要执行的任务。多个请求同时进行的时候,对网络数据回调的处理都是由AFNetworking的网络线程进行的。

多个线程任务之间的管理

有时候,可能需要在所有网络请求完成之后需要做一件事情或者是不同的网络请求直接还有一定的依赖关系或者是顺序关系,这种情况下需要使用GCD的group去处理。

GCD group简单介绍:

通过group可以对多个task进行统一管理,比如现在需要在多个任务完成之后再去做最后的处理:

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

dispatch_group_t group = dispatch_group_create();

dispatch_queue_t queue = dispatch_queue_create("com.ConcurrentQueue",
DISPATCH_QUEUE_CONCURRENT);

dispatch_group_async(group, queue, ^{

NSLog(@"task A");

});

dispatch_group_async(group, queue, ^{

NSLog(@"task B");

});

dispatch_group_notify(group, queue, ^{

NSLog(@"task LAST");

});

dispatch_group_async(group, queue, ^{

NSLog(@"task C");

});

输出

1
2
3
4
5
6
7
2016-01-13 19:18:13.814 operation+dispatch_group[13783:2280382] task B

2016-01-13 19:18:13.814 operation+dispatch_group[13783:2280385] task C

2016-01-13 19:18:13.814 operation+dispatch_group[13783:2280383] task A

2016-01-13 19:18:13.815 operation+dispatch_group[13783:2280383] task LAST

通过group让task LAST等到ABC任务执行完之后最后进行处理。

AFNetworking中多任务之间的管理实现

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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106

+ (NSArray *)batchOfRequestOperations:(NSArray *)operations
progressBlock:(void (^)(NSUInteger
numberOfFinishedOperations, NSUInteger
totalNumberOfOperations))progressBlock

completionBlock:(void (^)(NSArray *operations))
completionBlock

{

if (!operations || [operations count] == 0) {

return @[[NSBlockOperation blockOperationWithBlock:^{

dispatch_async(dispatch_get_main_queue(), ^{

if (completionBlock) {

completionBlock(@[]);

}

});

}]];

}

__block dispatch_group_t group = dispatch_group_create();


//等待所有任务都完成之后最后需要处理的任务
NSBlockOperation *batchedOperation = [NSBlockOperation
blockOperationWithBlock:^{
//通过dispatch_group_notify保证completionBlock当前group
//中的主线程队列上所有的originalCompletionBlock任务处理完之后再处理
dispatch_group_notify(group, dispatch_get_main_queue(), ^{

if (completionBlock) {

completionBlock(operations);

}

});

}];

for (AFURLConnectionOperation *operation in operations) {

operation.completionGroup = group;

void (^originalCompletionBlock)(void) = [operation.completionBlock copy];

__weak __typeof(operation)weakOperation = operation;
//改变每个Operation的completionBlock,主要是添加了能显示任务完成进度的功能。
operation.completionBlock = ^{

__strong __typeof(weakOperation)strongOperation = weakOperation;

//忽略行级别的编译器警告
#pragma clang diagnostic push

#pragma clang diagnostic ignored "-Wgnu"

dispatch_queue_t queue = strongOperation.completionQueue ?: dispatch_get_main_queue();

#pragma clang diagnostic pop

dispatch_group_async(group, queue, ^{
//originalCompletionBlock任务可能会被放到dispatch_main_queue中处理。
if (originalCompletionBlock) {

originalCompletionBlock();

}

//过滤出已经完成的任务
NSUInteger numberOfFinishedOperations = [[operations
indexesOfObjectsPassingTest:^BOOL(id op, NSUInteger __unused
idx, BOOL __unused *stop) {

return [op isFinished];

}] count];

if (progressBlock) {
//显示任务完成的进度
progressBlock(numberOfFinishedOperations, [operations count
]);

}
dispatch_group_leave(group);

});

};

dispatch_group_enter(group);
//添加batchedOperation对所有Operation任务的依赖,保证batchedOperation最后处理。
[batchedOperation addDependency:operation];

}
return [operations arrayByAddingObject:batchedOperation];
}

这个函数是将多个任务进行打包处理,并能显示出完成进度,以及最后等待所有任务完成之后进行最后的处理。