聚合国内IT技术精华文章,分享IT技术精华,帮助IT从业人士成长

AFNetworking2.0源码解析

2014-08-30 00:15 浏览: 1028712 次 我要评论(0 条) 字号:

最近看AFNetworking2的源码,学习这个知名网络框架的实现,顺便梳理写下文章。AFNetworking的代码还在不断更新中,我看的是AFNetworking2.3.1

本篇先看看AFURLConnectionOperation,AFURLConnectionOperation继承自NSOperation,是一个封装好的任务单元,在这里构建了NSURLConnection,作为NSURLConnection的delegate处理请求回调,做好状态切换,线程管理,可以说是AFNetworking最核心的类,下面分几部分说下看源码时注意的点,最后放上代码的注释。

0.Tricks

AFNetworking代码中有一些常用技巧,先说明一下。

A.clang warning

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
//code
#pragma clang diagnostic pop

表示在这个区间里忽略一些特定的clang的编译警告,因为AFNetworking作为一个库被其他项目引用,所以不能全局忽略clang的一些警告,只能在有需要的时候局部这样做,作者喜欢用?:符号,所以经常见忽略-Wgnu警告的写法,详见这里

B.dispatch_once

为保证线程安全,所有单例都用dispatch_once生成,保证只执行一次,这也是iOS开发常用的技巧。例如:

static dispatch_queue_t url_request_operation_completion_queue() {
    static dispatch_queue_t af_url_request_operation_completion_queue;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        af_url_request_operation_completion_queue = dispatch_queue_create("com.alamofire.networking.operation.queue",   DISPATCH_QUEUE_CONCURRENT );
    });
    return af_url_request_operation_completion_queue;
}

C.weak & strong self

常看到一个block要使用self,会处理成在外部声明一个weak变量指向self,在block里又声明一个strong变量指向weakSelf:

__weak __typeof(self)weakSelf = self;
self.backgroundTaskIdentifier = [application beginBackgroundTaskWithExpirationHandler:^{
    __strong __typeof(weakSelf)strongSelf = weakSelf;
}];

weakSelf是为了block不持有self,避免循环引用,而再声明一个strongSelf是因为一旦进入block执行,就不允许self在这个执行过程中释放。block执行完后这个strongSelf会自动释放,没有循环引用问题。

1.线程

先来看看NSURLConnection发送请求时的线程情况,NSURLConnection是被设计成异步发送的,调用了start方法后,NSURLConnection会新建一些线程用底层的CFSocket去发送和接收请求,在发送和接收的一些事件发生后通知原来线程的Runloop去回调事件。

NSURLConnection的同步方法sendSynchronousRequest方法也是基于异步的,同样要在其他线程去处理请求的发送和接收,只是同步方法会手动block住线程,发送状态的通知也不是通过RunLoop进行。

使用NSURLConnection有几种选择:

A.在主线程调异步接口

若直接在主线程调用异步接口,会有个Runloop相关的问题:

当在主线程调用[[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES]时,请求发出,侦听任务会加入到主线程的Runloop下,RunloopMode会默认为NSDefaultRunLoopMode。这表明只有当前线程的Runloop处于NSDefaultRunLoopMode时,这个任务才会被执行。但当用户滚动tableview或scrollview时,主线程的Runloop是处于NSEventTrackingRunLoopMode模式下的,不会执行NSDefaultRunLoopMode的任务,所以会出现一个问题,请求发出后,如果用户一直在操作UI上下滑动屏幕,那在滑动结束前是不会执行回调函数的,只有在滑动结束,RunloopMode切回NSDefaultRunLoopMode,才会执行回调函数。苹果一直把动画效果性能放在第一位,估计这也是苹果提升UI动画性能的手段之一。

所以若要在主线程使用NSURLConnection异步接口,需要手动把RunloopMode设为NSRunLoopCommonModes。这个mode意思是无论当前Runloop处于什么状态,都执行这个任务。

NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
[connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
[connection start];

B.在子线程调同步接口

若在子线程调用同步接口,一条线程只能处理一个请求,因为请求一发出去线程就阻塞住等待回调,需要给每个请求新建一个线程,这是很浪费的,这种方式唯一的好处应该是易于控制请求并发的数量。

C.在子线程调异步接口

子线程调用异步接口,子线程需要有Runloop去接收异步回调事件,这里也可以每个请求都新建一条带有Runloop的线程去侦听回调,但这一点好处都没有,既然是异步回调,除了处理回调内容,其他时间线程都是空闲可利用的,所有请求共用一个响应的线程就够了。

AFNetworking用的就是第三种方式,创建了一条常驻线程专门处理所有请求的回调事件,这个模型跟nodejs有点类似。网络请求回调处理完,组装好数据后再给上层调用者回调,这时候回调是抛回主线程的,因为主线程是最安全的,使用者可能会在回调中更新UI,在子线程更新UI会导致各种问题,一般使用者也可以不需要关心线程问题。

以下是相关线程大致的关系,实际上多个NSURLConnection会共用一个NSURLConnectionLoader线程,这里就不细化了,除了处理socket的CFSocket线程,还有一些Javascript:Core的线程,目前不清楚作用,归为NSURLConnection里的其他线程。因为NSURLConnection是系统控件,每个iOS版本可能都有不一样,可以先把NSURLConnection当成一个黑盒,只管它的start和callback就行了。如果使用AFHttpRequestOperationManager的接口发送请求,这些请求会统一在一个NSOperationQueue里去发,所以多了上面NSOperationQueue的一个线程。

afnetroking (2)

相关代码:-networkRequestThread:, -start:, -operationDidStart:。

2.状态机

继承NSOperation有个很麻烦的东西要处理,就是改变状态时需要发KVO通知,否则这个类加入NSOperationQueue不可用了。NSOperationQueue是用KVO方式侦听NSOperation状态的改变,以判断这个任务当前是否已完成,完成的任务需要在队列中除去并释放。

AFURLConnectionOperation对此做了个状态机,统一搞定状态切换以及发KVO通知的问题,内部要改变状态时,就只需要类似self.state = AFOperationReadyState的调用而不需要做其他了,状态改变的KVO通知在setState里发出。

总的来说状态管理相关代码就三部分,一是限制一个状态可以切换到其他哪些状态,避免状态切换混乱,二是状态Enum值与NSOperation四个状态方法的对应,三是在setState时统一发KVO通知。详见代码注释。

相关代码:AFKeyPathFromOperationState, AFStateTransitionIsValid, -setState:, -isPaused:, -isReady:, -isExecuting:, -isFinished:.

3.NSURLConnectionDelegate

处理NSURLConnection Delegate的内容不多,代码也是按请求回调的顺序排列下去,十分易读,主要流程就是接收到响应的时候打开outputStream,接着有数据过来就往outputStream写,在上传/接收数据过程中会回调上层传进来的相应的callback,在请求完成回调到connectionDidFinishLoading时,关闭outputStream,用outputStream组装responseData作为接收到的数据,把NSOperation状态设为finished,表示任务完成,NSOperation会自动调用completeBlock,再回调到上层。

4.setCompleteBlock

NSOperation在iOS4.0以后提供了个接口setCompletionBlock,可以传入一个block作为任务执行完成时(state状态机变为finished时)的回调,AFNetworking直接用了这个接口,并通过重写加了几个功能:

A.消除循环引用

在NSOperation的实现里,completionBlock是NSOperation对象的一个成员,NSOperation对象持有着completionBlock,若传进来的block用到了NSOperation对象,或者block用到的对象持有了这个NSOperation对象,就会造成循环引用。这里执行完block后调用[strongSelf setCompletionBlock:nil]把completionBlock设成nil,手动释放self(NSOperation对象)持有的completionBlock对象,打破循环引用。

可以理解成对外保证传进来的block一定会被释放,解决外部使用使很容易出现的因对象关系复杂导致循环引用的问题,让使用者不知道循环引用这个概念都能正确使用。

B.dispatch_group

这里允许用户让所有operation的completionBlock在一个group里执行,但我没看出这样做的作用,若想组装一组请求(见下面的batchOfRequestOperations)也不需要再让completionBlock在group里执行,求解。

C.”The Deallocation Problem”

作者在注释里说这里重写的setCompletionBlock方法解决了”The Deallocation Problem”,实际上并没有。”The Deallocation Problem”简单来说就是不要让UIKit的东西在子线程释放。

这里如果传进来的block持有了外部的UIViewController或其他UIKit对象(下面暂时称为A对象),并且在请求完成之前其他所有对这个A对象的引用都已经释放了,那么这个completionBlock就是最后一个持有这个A对象的,这个block释放时A对象也会释放。这个block在什么线程释放,A对象就会在什么线程释放。我们看到block释放的地方是url_request_operation_completion_queue(),这是AFNetworking特意生成的子线程,所以按理说A对象是会在子线程释放的,会导致UIKit对象在子线程释放,会有问题。

但AFNetworking实际用起来却没问题,想了很久不得其解,后来做了实验,发现iOS5以后苹果对UIKit对象的释放做了特殊处理,只要发现在子线程释放这些对象,就自动转到主线程去释放,断点出来是由一个叫_objc_deallocOnMainThreadHelper的方法做的。如果不是UIKit对象就不会跳到主线程释放。AFNetworking2.0只支持iOS6+,所以没问题。

blockTest

5.batchOfRequestOperations

这里额外提供了一个便捷接口,可以传入一组请求,在所有请求完成后回调complionBlock,在每一个请求完成时回调progressBlock通知外面有多少个请求已完成。详情参见代码注释,这里需要说明下dispatch_group_enter和dispatch_group_leave的使用,这两个方法用于把一个异步任务加入group里。

一般我们要把一个任务加入一个group里是这样:

dispatch_group_async(group, queue, ^{
    block();
});

这个写法等价于

dispatch_async(queue, ^{
    dispatch_group_enter(group);
    block()
    dispatch_group_leave(group);
});

如果要把一个异步任务加入group,这样就行不通了:

dispatch_group_async(group, queue, ^{
    [self performBlock:^(){
        block();
    }];
    //未执行到block() group任务就已经完成了
});

这时需要这样写:

dispatch_group_enter(group);
[self performBlock:^(){
    block();
    dispatch_group_leave(group);
}];

异步任务回调后才算这个group任务完成。对batchOfRequest的实现来说就是请求完成并回调后,才算这个任务完成。

其实这跟retain/release差不多,都是计数,dispatch_group_enter时任务数+1,dispatch_group_leave时任务数-1,任务数为0时执行dispatch_group_notify的内容。

相关代码:-batchOfRequestOperations:progressBlock:completionBlock:

6.其他

A.锁

AFURLConnectionOperation有一把递归锁,在所有会访问/修改成员变量的对外接口都加了锁,因为这些对外的接口用户是可以在任意线程调用的,对于访问和修改成员变量的接口,必须用锁保证线程安全。

B.序列化

AFNetworking的多数类都支持序列化,但实现的是NSSecureCoding的接口,而不是NSCoding,区别在于解数据时要指定Class,用-decodeObjectOfClass:forKey:方法代替了-decodeObjectForKey:。这样做更安全,因为序列化后的数据有可能被篡改,若不指定Class,-decode出来的对象可能不是原来的对象,有潜在风险。另外,NSSecureCoding是iOS6以上才有的。详见这里

这里在序列化时保存了当前任务状态,接收的数据等,但回调block是保存不了的,需要在取出来发送时重新设置。可以像下面这样持久化保存和取出任务:

AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:operation];

AFHTTPRequestOperation *operationFromDB = [NSKeyedUnarchiver unarchiveObjectWithData:data];
[operationFromDB start];

C.backgroundTask

这里提供了setShouldExecuteAsBackgroundTaskWithExpirationHandler接口,决定APP进入后台后是否继续发送接收请求,并在后台执行时间超时后取消所有请求。在dealloc里需要调用[application endBackgroundTask:],告诉系统这个后台任务已经完成,不然系统会一直让你的APP运行在后台,直到超时。

相关代码:-setShouldExecuteAsBackgroundTaskWithExpirationHandler:, -dealloc:

7.AFHTTPRequestOperation

AFHTTPRequestOperation继承了AFURLConnectionOperation,把它放一起说是因为它没做多少事情,主要多了responseSerializer,暂停下载断点续传,以及提供接口请求成功失败的回调接口-setCompletionBlockWithSuccess:failure:。详见源码注释。

8.源码注释

// AFURLConnectionOperation.m
//
// Copyright (c) 2013-2014 AFNetworking (http://afnetworking.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#import "AFURLConnectionOperation.h"

#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
#import <UIKit/UIKit.h>
#endif

#if !__has_feature(objc_arc)
#error AFNetworking must be built with ARC.
// You can turn on ARC for only AFNetworking files by adding -fobjc-arc to the build phase for each of its files.
#endif

typedef NS_ENUM(NSInteger, AFOperationState) {
    AFOperationPausedState      = -1,
    AFOperationReadyState       = 1,
    AFOperationExecutingState   = 2,
    AFOperationFinishedState    = 3,
};

#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && !defined(AF_APP_EXTENSIONS)
typedef UIBackgroundTaskIdentifier AFBackgroundTaskIdentifier;
#else
typedef id AFBackgroundTaskIdentifier;
#endif

static dispatch_group_t url_request_operation_completion_group() {
    static dispatch_group_t af_url_request_operation_completion_group;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        af_url_request_operation_completion_group = dispatch_group_create();
    });

    return af_url_request_operation_completion_group;
}

static dispatch_queue_t url_request_operation_completion_queue() {
    static dispatch_queue_t af_url_request_operation_completion_queue;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        af_url_request_operation_completion_queue = dispatch_queue_create("com.alamofire.networking.operation.queue", DISPATCH_QUEUE_CONCURRENT );
    });

    return af_url_request_operation_completion_queue;
}

static NSString * const kAFNetworkingLockName = @"com.alamofire.networking.operation.lock";

NSString * const AFNetworkingOperationDidStartNotification = @"com.alamofire.networking.operation.start";
NSString * const AFNetworkingOperationDidFinishNotification = @"com.alamofire.networking.operation.finish";

typedef void (^AFURLConnectionOperationProgressBlock)(NSUInteger bytes, long long totalBytes, long long totalBytesExpected);
typedef void (^AFURLConnectionOperationAuthenticationChallengeBlock)(NSURLConnection *connection, NSURLAuthenticationChallenge *challenge);
typedef NSCachedURLResponse * (^AFURLConnectionOperationCacheResponseBlock)(NSURLConnection *connection, NSCachedURLResponse *cachedResponse);
typedef NSURLRequest * (^AFURLConnectionOperationRedirectResponseBlock)(NSURLConnection *connection, NSURLRequest *request, NSURLResponse *redirectResponse);

///////////////状态机相关//////////////
static inline NSString * AFKeyPathFromOperationState(AFOperationState state) {
    //内部使用的枚举类型转成NSOperation使用的keyPath
    switch (state) {
        case AFOperationReadyState:
            return @"isReady";
        case AFOperationExecutingState:
            return @"isExecuting";
        case AFOperationFinishedState:
            return @"isFinished";
        case AFOperationPausedState:
            return @"isPaused";
        default: {
             //暂不明白为什么要加这部分,理论上不会走到这里
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunreachable-code"
            return @"state";
#pragma clang diagnostic pop
        }
    }
}

static inline BOOL AFStateTransitionIsValid(AFOperationState fromState, AFOperationState toState, BOOL isCancelled) {
    //规定可以从哪些状态切换到哪些状态
    switch (fromState) {
        case AFOperationReadyState:
            switch (toState) {
                case AFOperationPausedState:
                case AFOperationExecutingState:
                    return YES;
                case AFOperationFinishedState:
                    return isCancelled;
                default:
                    return NO;
            }
        case AFOperationExecutingState:
            switch (toState) {
                case AFOperationPausedState:
                case AFOperationFinishedState:
                    return YES;
                default:
                    return NO;
            }
        case AFOperationFinishedState:
            return NO;
        case AFOperationPausedState:
            return toState == AFOperationReadyState;
        default: {
            //暂不明白为什么要加这部分,理论上不会走到这里
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunreachable-code"
            switch (toState) {
                case AFOperationPausedState:
                case AFOperationReadyState:
                case AFOperationExecutingState:
                case AFOperationFinishedState:
                    return YES;
                default:
                    return NO;
            }
        }
#pragma clang diagnostic pop
    }
}

@interface AFURLConnectionOperation ()
@property (readwrite, nonatomic, assign) AFOperationState state;
@property (readwrite, nonatomic, strong) NSRecursiveLock *lock;
@property (readwrite, nonatomic, strong) NSURLConnection *connection;
@property (readwrite, nonatomic, strong) NSURLRequest *request;
@property (readwrite, nonatomic, strong) NSURLResponse *response;
@property (readwrite, nonatomic, strong) NSError *error;
@property (readwrite, nonatomic, strong) NSData *responseData;
@property (readwrite, nonatomic, copy) NSString *responseString;
@property (readwrite, nonatomic, assign) NSStringEncoding responseStringEncoding;
@property (readwrite, nonatomic, assign) long long totalBytesRead;
@property (readwrite, nonatomic, assign) AFBackgroundTaskIdentifier backgroundTaskIdentifier;
@property (readwrite, nonatomic, copy) AFURLConnectionOperationProgressBlock uploadProgress;
@property (readwrite, nonatomic, copy) AFURLConnectionOperationProgressBlock downloadProgress;
@property (readwrite, nonatomic, copy) AFURLConnectionOperationAuthenticationChallengeBlock authenticationChallenge;
@property (readwrite, nonatomic, copy) AFURLConnectionOperationCacheResponseBlock cacheResponse;
@property (readwrite, nonatomic, copy) AFURLConnectionOperationRedirectResponseBlock redirectResponse;

- (void)operationDidStart;
- (void)finish;
- (void)cancelConnection;
@end

@implementation AFURLConnectionOperation
@synthesize outputStream = _outputStream;

//AFNetworking线程的主体,处理NSURLConnection回调的专用线程
+ (void)networkRequestThreadEntryPoint:(id)__unused object {
    @autoreleasepool {
        [[NSThread currentThread] setName:@"AFNetworking"];
        //给线程创建一个Runloop,永不退出。
        NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
        //因为不是主线程,mode是NSDefaultRunLoopMode也没关系
        [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
        [runLoop run];
    }
}

+ (NSThread *)networkRequestThread {
    //获取AFNetworking线程,单例模式,dispatch_once是生成单例的常见模式,确保线程安全
    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;
}

- (instancetype)initWithRequest:(NSURLRequest *)urlRequest {
    NSParameterAssert(urlRequest);

    self = [super init];
    if (!self) {
		return nil;
    }

    _state = AFOperationReadyState;

    self.lock = [[NSRecursiveLock alloc] init];
    self.lock.name = kAFNetworkingLockName;

    self.runLoopModes = [NSSet setWithObject:NSRunLoopCommonModes];

    self.request = urlRequest;

    self.shouldUseCredentialStorage = YES;

    self.securityPolicy = [AFSecurityPolicy defaultPolicy];

    return self;
}

- (void)dealloc {
    if (_outputStream) {
        [_outputStream close];
        _outputStream = nil;
    }

#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && !defined(AF_APP_EXTENSIONS)
    if (_backgroundTaskIdentifier) {
        //APP在后台,任务在后台运行,需要告诉系统任务已完成,不然系统会一直让APP运行在后台,直到超时。
        [[UIApplication sharedApplication] endBackgroundTask:_backgroundTaskIdentifier];
        _backgroundTaskIdentifier = UIBackgroundTaskInvalid;
    }
#endif
}

#pragma mark -

- (void)setResponseData:(NSData *)responseData {
    [self.lock lock];
    if (!responseData) {
        _responseData = nil;
    } else {
        _responseData = [NSData dataWithBytes:responseData.bytes length:responseData.length];
    }
    [self.lock unlock];
}

- (NSString *)responseString {
    [self.lock lock];
    //第一次访问才生成数据
    if (!_responseString && self.response && self.responseData) {
        self.responseString = [[NSString alloc] initWithData:self.responseData encoding:self.responseStringEncoding];
    }
    [self.lock unlock];

    return _responseString;
}

- (NSStringEncoding)responseStringEncoding {
    [self.lock lock];
    //同样第一次访问才生成数据
    if (!_responseStringEncoding && self.response) {
        //默认是UTF8编码
        NSStringEncoding stringEncoding = NSUTF8StringEncoding;
        //若NSHTTPURLResponse解析到返回的HTTP头部带上了编码类型,就用这个编码类型
        if (self.response.textEncodingName) {
            CFStringEncoding IANAEncoding = CFStringConvertIANACharSetNameToEncoding((__bridge CFStringRef)self.response.textEncodingName);
            if (IANAEncoding != kCFStringEncodingInvalidId) {
                stringEncoding = CFStringConvertEncodingToNSStringEncoding(IANAEncoding);
            }
        }
        self.responseStringEncoding = stringEncoding;
    }
    [self.lock unlock];

    return _responseStringEncoding;
}

- (NSInputStream *)inputStream {
    return self.request.HTTPBodyStream;
}

- (void)setInputStream:(NSInputStream *)inputStream {
    NSMutableURLRequest *mutableRequest = [self.request mutableCopy];
    mutableRequest.HTTPBodyStream = inputStream;
    self.request = mutableRequest;
}

- (NSOutputStream *)outputStream {
    if (!_outputStream) {
        //默认的outputStream是保存到memory的,下载大文件有撑爆内存的风险
        //所以下载大文件需要自己调下面的setOutputStream设保存到文件的outputStream
        self.outputStream = [NSOutputStream outputStreamToMemory];
    }

    return _outputStream;
}

- (void)setOutputStream:(NSOutputStream *)outputStream {
    [self.lock lock];
    if (outputStream != _outputStream) {
        if (_outputStream) {
            [_outputStream close];
        }

        _outputStream = outputStream;
    }
    [self.lock unlock];
}

#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && !defined(AF_APP_EXTENSIONS)
//APP退出到后台时继续发送接收请求
- (void)setShouldExecuteAsBackgroundTaskWithExpirationHandler:(void (^)(void))handler {
    //这个是外部接口,又读写了成员backgroundTaskIdentifier,所以要加锁
    [self.lock lock];
    if (!self.backgroundTaskIdentifier) {
        UIApplication *application = [UIApplication sharedApplication];
        __weak __typeof(self)weakSelf = self;
        //一个任务一个identifier,用于调用endBackgroundTask
        self.backgroundTaskIdentifier = [application beginBackgroundTaskWithExpirationHandler:^{
            //后台执行超时,APP将要休眠时会调用这个block

            //这个strongSelf应该是没必要的,就算block执行过程中self被释放也没什么问题。
            //何况下面使用strongSelf时还加了判断
            __strong __typeof(weakSelf)strongSelf = weakSelf;

            if (handler) {
                handler();
            }

            if (strongSelf) {
                [strongSelf cancel];

                [application endBackgroundTask:strongSelf.backgroundTaskIdentifier];
                strongSelf.backgroundTaskIdentifier = UIBackgroundTaskInvalid;
            }
        }];
    }
    [self.lock unlock];
}
#endif

#pragma mark -
/////////////////状态机相关/////////////////
- (void)setState:(AFOperationState)state {
    if (!AFStateTransitionIsValid(self.state, state, [self isCancelled])) {
        return;
    }

    [self.lock lock];
    NSString *oldStateKey = AFKeyPathFromOperationState(self.state);
    NSString *newStateKey = AFKeyPathFromOperationState(state);

    //切换状态时,旧状态和新状态都需要发KVO通知。
    //例如从isPaused切到isExecuting,需要让外部知道isPaused从YES变NO,isExecuting从NO变YES
    [self willChangeValueForKey:newStateKey];
    [self willChangeValueForKey:oldStateKey];
    _state = state;
    //枚举变量改变后,外部调用isPaused,isExecuting等方法返回的值就改变了,发出didChangeValue事件。
    [self didChangeValueForKey:oldStateKey];
    [self didChangeValueForKey:newStateKey];
    [self.lock unlock];
}

- (void)pause {
    if ([self isPaused] || [self isFinished] || [self isCancelled]) {
        return;
    }

    [self.lock lock];

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

        dispatch_async(dispatch_get_main_queue(), ^{
            //统一规则:notification都在主线程发出
            NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
            [notificationCenter postNotificationName:AFNetworkingOperationDidFinishNotification object:self];
        });
    }

    self.state = AFOperationPausedState;

    [self.lock unlock];
}

- (void)operationDidPause {
    [self.lock lock];
    [self.connection cancel];
    [self.lock unlock];
}

- (BOOL)isPaused {
    return self.state == AFOperationPausedState;
}

- (void)resume {
    if (![self isPaused]) {
        return;
    }

    [self.lock lock];
    self.state = AFOperationReadyState;

    [self start];
    [self.lock unlock];
}

#pragma mark -

- (void)setUploadProgressBlock:(void (^)(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite))block {
    self.uploadProgress = block;
}

- (void)setDownloadProgressBlock:(void (^)(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead))block {
    self.downloadProgress = block;
}

- (void)setWillSendRequestForAuthenticationChallengeBlock:(void (^)(NSURLConnection *connection, NSURLAuthenticationChallenge *challenge))block {
    self.authenticationChallenge = block;
}

- (void)setCacheResponseBlock:(NSCachedURLResponse * (^)(NSURLConnection *connection, NSCachedURLResponse *cachedResponse))block {
    self.cacheResponse = block;
}

- (void)setRedirectResponseBlock:(NSURLRequest * (^)(NSURLConnection *connection, NSURLRequest *request, NSURLResponse *redirectResponse))block {
    self.redirectResponse = block;
}

#pragma mark - NSOperation

- (void)setCompletionBlock:(void (^)(void))block {
    [self.lock lock];
    if (!block) {
        //用于把block置nil消除循环引用
        [super setCompletionBlock:nil];
    } else {
        __weak __typeof(self)weakSelf = self;
        [super setCompletionBlock:^ {
            __strong __typeof(weakSelf)strongSelf = weakSelf;

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
            dispatch_group_t group = strongSelf.completionGroup ?: url_request_operation_completion_group();
            dispatch_queue_t queue = strongSelf.completionQueue ?: dispatch_get_main_queue();
#pragma clang diagnostic pop

            //让传进来的completionBlock在一个group里执行,目前不清楚这样做的意义
            //若外部使用要把一堆请求放在一个group里,这里也不需要让completionBlock在这个group里执行
            //详见batchOfRequestOperations的实现
            dispatch_group_async(group, queue, ^{
                block();
            });

            //这里在url_request_operation_completion_queue()释放block在iOS5以下可能会导致"The Deallocation Problem"
            //但AFNetworking2.0只支持iOS6+,所以没问题
            //但还是不清除这里为什么专门用一条线程去释放completionBlock
            dispatch_group_notify(group, url_request_operation_completion_queue(), ^{
                [strongSelf setCompletionBlock:nil];
            });
        }];
    }
    [self.lock unlock];
}

- (BOOL)isReady {
    return self.state == AFOperationReadyState && [super isReady];
}

- (BOOL)isExecuting {
    return self.state == AFOperationExecutingState;
}

- (BOOL)isFinished {
    return self.state == AFOperationFinishedState;
}

- (BOOL)isConcurrent {
    return YES;
}

- (void)start {
    //start是公开方法,可以在外部被任意线程调用,因为这里要修改self.state,加锁保证线程安全
    [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线程上执行这个任务
        [self performSelector:@selector(operationDidStart) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
    }
    [self.lock unlock];
}

- (void)operationDidStart {
    [self.lock lock];
    if (![self isCancelled]) {
        self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO];
        //在当前Runloop加上对connection和outputStream事件的侦听,回调就会在当前线程(AFNetworking)执行
        NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
        for (NSString *runLoopMode in self.runLoopModes) {
            [self.connection scheduleInRunLoop:runLoop forMode:runLoopMode];
            [self.outputStream scheduleInRunLoop:runLoop forMode:runLoopMode];
        }

        [self.connection start];
    }
    [self.lock unlock];

    dispatch_async(dispatch_get_main_queue(), ^{
        //统一规则:notification都在主线程发出
        [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingOperationDidStartNotification object:self];
    });
}

- (void)finish {
    [self.lock lock];
    //在setState方法里会发出KVO通知,NSOperationQueue接收到finished的通知就会把当前任务释放了
    self.state = AFOperationFinishedState;
    [self.lock unlock];

    dispatch_async(dispatch_get_main_queue(), ^{
        //统一规则:notification都在主线程发出
        [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingOperationDidFinishNotification object:self];
    });
}

- (void)cancel {
    [self.lock lock];
    if (![self isFinished] && ![self isCancelled]) {
        [super cancel];

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

- (void)cancelConnection {
    NSDictionary *userInfo = nil;
    if ([self.request URL]) {
        userInfo = [NSDictionary dictionaryWithObject:[self.request URL] forKey:NSURLErrorFailingURLErrorKey];
    }
    NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCancelled userInfo:userInfo];

    if (![self isFinished]) {
        if (self.connection) {
            [self.connection cancel];
            [self performSelector:@selector(connection:didFailWithError:) withObject:self.connection withObject:error];
        } else {
            // Accomodate race condition where `self.connection` has not yet been set before cancellation
            //start和cancel可能在外部由不同线程调用,有可能出现cancel在start前执行,这时self.connection还没构建
            self.error = error;
            [self finish];
        }
    }
}

#pragma mark -
//工具方法,传入一组请求,在所有请求完成后回调complionBlock
+ (NSArray *)batchOfRequestOperations:(NSArray *)operations
                        progressBlock:(void (^)(NSUInteger numberOfFinishedOperations, NSUInteger totalNumberOfOperations))progressBlock
                      completionBlock:(void (^)(NSArray *operations))completionBlock
{
    if (!operations || [operations count] == 0) {
        //传个空数组进来,直接构建个回调completionBlock的NSOperation
        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(), ^{
            //group所有任务完成后执行completionBlock
            if (completionBlock) {
                completionBlock(operations);
            }
        });
    }];

    for (AFURLConnectionOperation *operation in operations) {
        operation.completionGroup = group;
        void (^originalCompletionBlock)(void) = [operation.completionBlock copy];
        __weak __typeof(operation)weakOperation = operation;
        //在completionBlock加上progressBlock的回调,调用dispatch_group_leave通知group任务完成。
        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, ^{
                //执行原来的completionBlock
                if (originalCompletionBlock) {
                    originalCompletionBlock();
                }

                //获取已完成的任务数,回调progressBlock
                NSUInteger numberOfFinishedOperations = [[operations indexesOfObjectsPassingTest:^BOOL(id op, NSUInteger __unused idx,  BOOL __unused *stop) {
                    return [op isFinished];
                }] count];

                if (progressBlock) {
                    progressBlock(numberOfFinishedOperations, [operations count]);
                }

                //请求完成,结束group任务,group任务数-1,最后一个请求完成时就会调用上面dispatch_group_notify里的completionBlock啦
                dispatch_group_leave(group);
            });
        };

        //开始group任务,group任务数+1,个人感觉这个dispatch_group_enter写在上面更好看。
        dispatch_group_enter(group);

        //添加依赖,执行完每一个operation后再执行batchedOperation
        //若operation里的实现是同步的,一个operation一个线程的话,用addDependency就可以解决所有请求完成后回调completionBlock的需求
        //但这里operation的实现是异步的,执行完operation只是构建好请求任务,所以还需借助dispatch_group才能知道所有请求完成的消息。
        [batchedOperation addDependency:operation];
    }

    return [operations arrayByAddingObject:batchedOperation];
}

#pragma mark - NSObject

- (NSString *)description {
    //直接用NSLog打这个对象出来时显示的内容
    return [NSString stringWithFormat:@"<%@: %p, state: %@, cancelled: %@ request: %@, response: %@>", NSStringFromClass([self class]), self, AFKeyPathFromOperationState(self.state), ([self isCancelled] ? @"YES" : @"NO"), self.request, self.response];
}

#pragma mark - NSURLConnectionDelegate

- (void)connection:(NSURLConnection *)connection
willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
    if (self.authenticationChallenge) {
        self.authenticationChallenge(connection, challenge);
        return;
    }

    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
        if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
            NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
            [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
        } else {
            [[challenge sender] cancelAuthenticationChallenge:challenge];
        }
    } else {
        if ([challenge previousFailureCount] == 0) {
            if (self.credential) {
                [[challenge sender] useCredential:self.credential forAuthenticationChallenge:challenge];
            } else {
                [[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge];
            }
        } else {
            [[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge];
        }
    }
}

- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection __unused *)connection {
    return self.shouldUseCredentialStorage;
}

- (NSURLRequest *)connection:(NSURLConnection *)connection
             willSendRequest:(NSURLRequest *)request
            redirectResponse:(NSURLResponse *)redirectResponse
{
    if (self.redirectResponse) {
        //非主线程回调,因为要返回NSURLRequest,使用时得注意
        return self.redirectResponse(connection, request, redirectResponse);
    } else {
        return request;
    }
}

- (void)connection:(NSURLConnection __unused *)connection
   didSendBodyData:(NSInteger)bytesWritten
 totalBytesWritten:(NSInteger)totalBytesWritten
totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite
{
    dispatch_async(dispatch_get_main_queue(), ^{
        if (self.uploadProgress) {
            self.uploadProgress((NSUInteger)bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
        }
    });
}

- (void)connection:(NSURLConnection __unused *)connection
didReceiveResponse:(NSURLResponse *)response
{
    self.response = response;

    //开始接收数据
    [self.outputStream open];
}

- (void)connection:(NSURLConnection __unused *)connection
    didReceiveData:(NSData *)data
{
    NSUInteger length = [data length];
    while (YES) {
        NSInteger totalNumberOfBytesWritten = 0;
        if ([self.outputStream hasSpaceAvailable]) {
            const uint8_t *dataBuffer = (uint8_t *)[data bytes];

            NSInteger numberOfBytesWritten = 0;
            while (totalNumberOfBytesWritten < (NSInteger)length) {
                //循环写数据到outputStream,直到全部写完或者出错
                numberOfBytesWritten = [self.outputStream write:&dataBuffer[(NSUInteger)totalNumberOfBytesWritten] maxLength:(length - (NSUInteger)totalNumberOfBytesWritten)];
                if (numberOfBytesWritten == -1) {
                    break;
                }

                totalNumberOfBytesWritten += numberOfBytesWritten;
            }

            break;
        }

        if (self.outputStream.streamError) {
            [self.connection cancel];
            [self performSelector:@selector(connection:didFailWithError:) withObject:self.connection withObject:self.outputStream.streamError];
            return;
        }
    }

    dispatch_async(dispatch_get_main_queue(), ^{
        self.totalBytesRead += (long long)length;

        if (self.downloadProgress) {
            self.downloadProgress(length, self.totalBytesRead, self.response.expectedContentLength);
        }
    });
}

- (void)connectionDidFinishLoading:(NSURLConnection __unused *)connection {
    self.responseData = [self.outputStream propertyForKey:NSStreamDataWrittenToMemoryStreamKey];

    [self.outputStream close];
    if (self.responseData) {
       //解析完数据释放outputStream
       self.outputStream = nil;
    }

    self.connection = nil;

    [self finish];
}

- (void)connection:(NSURLConnection __unused *)connection
  didFailWithError:(NSError *)error
{
    self.error = error;

    [self.outputStream close];
    if (self.responseData) {
        self.outputStream = nil;
    }

    self.connection = nil;

    [self finish];
}

- (NSCachedURLResponse *)connection:(NSURLConnection *)connection
                  willCacheResponse:(NSCachedURLResponse *)cachedResponse
{
    if (self.cacheResponse) {
        //非主线程回调,因为要返回NSCachedURLResponse,使用时得注意
        return self.cacheResponse(connection, cachedResponse);
    } else {
        if ([self isCancelled]) {
            return nil;
        }

        return cachedResponse;
    }
}

#pragma mark - NSSecureCoding

+ (BOOL)supportsSecureCoding {
    //表明实现NSSecureCoding接口
    return YES;
}

- (id)initWithCoder:(NSCoder *)decoder {
    //使用-decodeObjectOfClass:forKey:安全解析数据,NSSecureCoding的一部分。
    NSURLRequest *request = [decoder decodeObjectOfClass:[NSURLRequest class] forKey:NSStringFromSelector(@selector(request))];

    self = [self initWithRequest:request];
    if (!self) {
        return nil;
    }

    self.state = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(state))] integerValue];
    self.response = [decoder decodeObjectOfClass:[NSHTTPURLResponse class] forKey:NSStringFromSelector(@selector(response))];
    self.error = [decoder decodeObjectOfClass:[NSError class] forKey:NSStringFromSelector(@selector(error))];
    self.responseData = [decoder decodeObjectOfClass:[NSData class] forKey:NSStringFromSelector(@selector(responseData))];
    self.totalBytesRead = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(totalBytesRead))] longLongValue];

    return self;
}

- (void)encodeWithCoder:(NSCoder *)coder {
    [self pause];

    [coder encodeObject:self.request forKey:NSStringFromSelector(@selector(request))];

    switch (self.state) {
        case AFOperationExecutingState:
        case AFOperationPausedState:
            [coder encodeInteger:AFOperationReadyState forKey:NSStringFromSelector(@selector(state))];
            break;
        default:
            [coder encodeInteger:self.state forKey:NSStringFromSelector(@selector(state))];
            break;
    }

    [coder encodeObject:self.response forKey:NSStringFromSelector(@selector(response))];
    [coder encodeObject:self.error forKey:NSStringFromSelector(@selector(error))];
    [coder encodeObject:self.responseData forKey:NSStringFromSelector(@selector(responseData))];
    [coder encodeInt64:self.totalBytesRead forKey:NSStringFromSelector(@selector(totalBytesRead))];
}

#pragma mark - NSCopying

- (id)copyWithZone:(NSZone *)zone {
    //
    AFURLConnectionOperation *operation = [(AFURLConnectionOperation *)[[self class] allocWithZone:zone] initWithRequest:self.request];

    operation.uploadProgress = self.uploadProgress;
    operation.downloadProgress = self.downloadProgress;
    operation.authenticationChallenge = self.authenticationChallenge;
    operation.cacheResponse = self.cacheResponse;
    operation.redirectResponse = self.redirectResponse;
    operation.completionQueue = self.completionQueue;
    operation.completionGroup = self.completionGroup;

    return operation;
}

@end
// AFHTTPRequestOperation.m
//
// Copyright (c) 2013-2014 AFNetworking (http://afnetworking.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
 
#import "AFHTTPRequestOperation.h"
 
static dispatch_queue_t http_request_operation_processing_queue() {
    static dispatch_queue_t af_http_request_operation_processing_queue;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        af_http_request_operation_processing_queue = dispatch_queue_create("com.alamofire.networking.http-request.processing", DISPATCH_QUEUE_CONCURRENT);
    });
 
    return af_http_request_operation_processing_queue;
}
 
static dispatch_group_t http_request_operation_completion_group() {
    static dispatch_group_t af_http_request_operation_completion_group;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        af_http_request_operation_completion_group = dispatch_group_create();
    });
 
    return af_http_request_operation_completion_group;
}
 
#pragma mark -
 
@interface AFURLConnectionOperation ()
@property (readwrite, nonatomic, strong) NSURLRequest *request;
@property (readwrite, nonatomic, strong) NSURLResponse *response;
@end
 
@interface AFHTTPRequestOperation ()
@property (readwrite, nonatomic, strong) NSHTTPURLResponse *response;
@property (readwrite, nonatomic, strong) id responseObject;
@property (readwrite, nonatomic, strong) NSError *responseSerializationError;
@property (readwrite, nonatomic, strong) NSRecursiveLock *lock;
@end
 
@implementation AFHTTPRequestOperation
@dynamic lock;
 
- (instancetype)initWithRequest:(NSURLRequest *)urlRequest {
    self = [super initWithRequest:urlRequest];
    if (!self) {
        return nil;
    }
    //默认使用AFHTTPResponseSerializer,只能解析错误信息
    self.responseSerializer = [AFHTTPResponseSerializer serializer];
 
    return self;
}
 
- (void)setResponseSerializer:(AFHTTPResponseSerializer  *)responseSerializer {
    NSParameterAssert(responseSerializer);
 
    [self.lock lock];
    _responseSerializer = responseSerializer;
    self.responseObject = nil;
    self.responseSerializationError = nil;
    [self.lock unlock];
}
 
- (id)responseObject {
    [self.lock lock];
    //第一次读取时用responseSerializer解析数据,生成responseObject
    if (!_responseObject && [self isFinished] && !self.error) {
        NSError *error = nil;
        self.responseObject = [self.responseSerializer responseObjectForResponse:self.response data:self.responseData error:&error];
        if (error) {
            self.responseSerializationError = error;
        }
    }
    [self.lock unlock];
 
    return _responseObject;
}
 
- (NSError *)error {
    if (_responseSerializationError) {
        return _responseSerializationError;
    } else {
        return [super error];
    }
}
 
#pragma mark - AFHTTPRequestOperation
 
- (void)setCompletionBlockWithSuccess:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
                              failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
{
    // completionBlock is manually nilled out in AFURLConnectionOperation to break the retain cycle.
    //父类setCompletionBlock的实现里解决了循环引用问题,这里在block里直接用self也没问题
    //不过编译器会警告retain-cycles,所以得加-Warc-retain-cycles
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-retain-cycles"
#pragma clang diagnostic ignored "-Wgnu"
    self.completionBlock = ^{
        //这里又让回调在group里运行,感觉为了加group,下面这些代码可读性低了点,跟父类的setCompletionBlock一样,看不出这个group的作用。
        if (self.completionGroup) {
            dispatch_group_enter(self.completionGroup);
        }
 
        dispatch_async(http_request_operation_processing_queue(), ^{
            if (self.error) {
                if (failure) {
                    dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{
                        //默认回调到主线程,下同
                        failure(self, self.error);
                    });
                }
            } else {
                id responseObject = self.responseObject;
                //第一次读取self.responseObject时会构建responseObject,构建过程中可能会出现错误,所以这里继续判断self.error是否有错误
                if (self.error) {
                    if (failure) {
                        dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{
                            failure(self, self.error);
                        });
                    }
                } else {
                    if (success) {
                        dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{
                            success(self, responseObject);
                        });
                    }
                }
            }
 
            if (self.completionGroup) {
                dispatch_group_leave(self.completionGroup);
            }
        });
    };
#pragma clang diagnostic pop
}
 
#pragma mark - AFURLRequestOperation
//下载暂停时提供断点续传功能,修改请求的HTTP头,记录当前下载的文件位置,下次可以从这个位置开始下载。
- (void)pause {
    [super pause];
 
    u_int64_t offset = 0;
    if ([self.outputStream propertyForKey:NSStreamFileCurrentOffsetKey]) {
        offset = [(NSNumber *)[self.outputStream propertyForKey:NSStreamFileCurrentOffsetKey] unsignedLongLongValue];
    } else {
        offset = [(NSData *)[self.outputStream propertyForKey:NSStreamDataWrittenToMemoryStreamKey] length];
    }
 
    NSMutableURLRequest *mutableURLRequest = [self.request mutableCopy];
    if ([self.response respondsToSelector:@selector(allHeaderFields)] && [[self.response allHeaderFields] valueForKey:@"ETag"]) {
        //若请求返回的头部有ETag,则续传时要带上这个ETag,ETag用于放置文件的唯一标识,比如文件MD5值
        //续传时带上ETag服务端可以校验相对上次请求,文件有没有变化,若有变化则返回200,回应新文件的全数据,若无变化则返回206续传。
        [mutableURLRequest setValue:[[self.response allHeaderFields] valueForKey:@"ETag"] forHTTPHeaderField:@"If-Range"];
    }
    //给当前request加Range头部,下次请求带上头部,可以从offset位置继续下载
    [mutableURLRequest setValue:[NSString stringWithFormat:@"bytes=%llu-", offset] forHTTPHeaderField:@"Range"];
    self.request = mutableURLRequest;
}
 
#pragma mark - NSSecureCoding
 
+ (BOOL)supportsSecureCoding {
    return YES;
}
 
- (id)initWithCoder:(NSCoder *)decoder {
    self = [super initWithCoder:decoder];
    if (!self) {
        return nil;
    }
 
    self.responseSerializer = [decoder decodeObjectOfClass:[AFHTTPResponseSerializer class] forKey:NSStringFromSelector(@selector(responseSerializer))];
 
    return self;
}
 
- (void)encodeWithCoder:(NSCoder *)coder {
    [super encodeWithCoder:coder];
 
    [coder encodeObject:self.responseSerializer forKey:NSStringFromSelector(@selector(responseSerializer))];
}
 
#pragma mark - NSCopying
 
- (id)copyWithZone:(NSZone *)zone {
    AFHTTPRequestOperation *operation = [super copyWithZone:zone];
 
    operation.responseSerializer = [self.responseSerializer copyWithZone:zone];
    operation.completionQueue = self.completionQueue;
    operation.completionGroup = self.completionGroup;
 
    return operation;
}
 
@end


网友评论已有0条评论, 我也要评论

发表评论

*

* (保密)

Ctrl+Enter 快捷回复