基本内容
本节主要介绍iOS中多线程相关的内容,先简单介绍NSOperation的使用,然后结合GCD实现任务之间的管理,比如每个任务完成之后的处理以及任务与任务之间的依赖,所有任务完成之后的处理等,通过AFNetworking源码分析其具体实现。
 
NSoperation
 通过NSOpeartion去完成某个任务的时候,有3种方式:
- NSInvocationOperation
- NSBlockOperation
- 自己继承NSOperation实现具体的任务操作
NSInvocationOperation
| 12
 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];
 
 
 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]);
 
 }
 
 | 
输出:
| 12
 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)}
 
 | 
从日志中看出任务分别在不同的线程中执行
| 12
 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];
 
 | 
输出:
| 12
 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对象之间的依赖,让每个任务之间按一定的顺序执行
| 12
 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];
 
 | 
输出:
| 12
 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
| 12
 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];
 
 | 
输出:
| 12
 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
| 12
 
 | @interface AFURLConnectionOperation : NSOperation <NSURLConnectionDelegate, NSURLConnectionDataDelegate, NSSecureCoding, NSCopying>
 
 | 
AFURLConnectionOperation是继承自NSOperation
AFURLConnectionOperation.m
| 12
 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;
 
 [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"];
 
 NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
 
 [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
 
 [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) {
 
 [self.connection scheduleInRunLoop:runLoop forMode:runLoopMode];
 
 [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进行统一管理,比如现在需要在多个任务完成之后再去做最后的处理:
| 12
 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");
 
 });
 
 | 
输出
| 12
 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中多任务之间的管理实现
| 12
 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(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 = ^{
 
 __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, ^{
 
 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 addDependency:operation];
 
 }
 return [operations arrayByAddingObject:batchedOperation];
 }
 
 | 
这个函数是将多个任务进行打包处理,并能显示出完成进度,以及最后等待所有任务完成之后进行最后的处理。