AFNetworking-NSOperation

基本内容

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

NSoperation

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

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

NSInvocationOperation

- (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]);

}
复制代码

输出:

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)}
复制代码

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

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];
复制代码

输出:

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对象之间的依赖,让每个任务之间按一定的顺序执行

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];
复制代码

输出:

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

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];
复制代码

输出:


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

@interface AFURLConnectionOperation : NSOperation <NSURLConnectionDelegate, 
NSURLConnectionDataDelegate, NSSecureCoding, NSCopying>
复制代码

AFURLConnectionOperation是继承自NSOperation

AFURLConnectionOperation.m


- (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进行统一管理,比如现在需要在多个任务完成之后再去做最后的处理:


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");

});
复制代码

输出

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中多任务之间的管理实现

+ (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];
}
复制代码

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值