源码解析之AFNetworking3.x(一)

源码分析一共分为三个部分:

  1. 介绍 AFNetworking 发起网络请求的主流程;
  2. 介绍 AFNetworking 网络安全、网络状态及其他类实现细节;
  3. 介绍 AFNetworking 中涉及到的分类;

源码解析之AFNetworking3.x(一)
源码解析之AFNetworking3.x(二)
源码解析之AFNetworking3.x(三)

本文主要分析利用 AFNetworking 发请网络请求的主流程,主要内容包括:

  1. 概述
  2. AFHTTPSessionManager 初始化
  3. NSURLRequest 的创建
  4. NSURLSessionDataTask 的创建
  5. 请求回调的处理

1. 概述

AFNetworking 框架基于 NSURLSession,如果对 NSURLSession 不太熟悉,戳这里。先祭出 AFNetworking 的结构图。

从图中可以看出,业务层通过AFHTTPSessionManager发起网络请求,AFURLSessionManager封装网络请求的核心功能,包括NSURLSession和请求代理的处理。请求参数序列化、响应数据序列化、网络安全验证和网络状态的监控分别通过类进行功能的封装。

分析 AFNetworking 前,先回顾下 NSURLSession 发起网络请求的流程:

  1. 创建NSURLRequest
  2. 处理请求参数和 HTTPHeader
  3. 创建NSURLSession
  4. 通过NSURLSession创建NSURLSessionTask
  5. 启动NSURLSessionTask
  6. 设置网络回调,处理 respone 信息;

AFNetworkingNSURLSession 封装后,请求流程相对简单很多。如下述代码所示,主要工作变成了创建AFHTTPSessionManager,调用相关方法发送网络请求,并设置请求参数、请求成功/失败的回调。

NSURL *url = [[NSURL alloc] initWithString:@"www.baidu.com"];
AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc] initWithBaseURL:url];
[manager GET:@"test.json" parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
    NSLog(@"%@" ,responseObject);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
    NSLog(@"%@", error);
}];

接下来本文会依照网络请求的发起到接收流程,分析 AFNetworking 的处理过程。

2. AFHTTPSessionManager 初始化

AFHTTPSessionManager继承自AFURLSessionManagerAFURLSessionManager封装了NSURLSession的基础功能,而 AFHTTPSessionManager则实现了 HTTP 请求相关的配置。我们从 AFHTTPSessionManager的头文件可以看出,其主要定义了各种网络请求方法。

AFHTTPSessionManager所有的初始化方法最终都会调到initWithBaseURL: sessionConfiguration:。该方法首先调用父类进行初始化,接着处理 url,最后初始化requestSerializerresponseSerializer

细心的同学可能注意到,子类和父类都有self.responseSerializer = [AFJSONResponseSerializer serializer];,实现的功能相同,为什么子类还要初始化一次呢。我想这得从框架结构的角度来分析,子类不能依赖父类responseSerializer的初始化。因为AFURLSessionManager作为父类,提供的是通用功能,目前仅有一个子类。但如果后期有多个子类呢,父类中的responseSerializer可能会采用不同的方式实现,这也是为什么子类和父类中responseSerializer类型不同的原因。父类的responseSerializer是实现了AFURLResponseSerialization接口的 id 类型,而父类是实现了AFURLResponseSerialization接口的AFHTTPResponseSerializer类型。

- (instancetype)initWithBaseURL:(NSURL *)url
           sessionConfiguration:(NSURLSessionConfiguration *)configuration
{
    self = [super initWithSessionConfiguration:configuration];
    if (!self) {
        return nil;
    }
    // 保证 url 以 '/' 结尾
    if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) {
        url = [url URLByAppendingPathComponent:@""];
    }
    self.baseURL = url;
    self.requestSerializer = [AFHTTPRequestSerializer serializer];
    self.responseSerializer = [AFJSONResponseSerializer serializer];
    return self;
}

父类初始化方法处理的内容则相对较多:包括NSURLSession、响应queueresponseSerializersecurityPolicy的初始化。其中较重要的是响应queue的处理和getTasksWithCompletionHandler:的调用。

通常情况下 AFNetworking 接口的调用包括resume方法的调用都在主线程中。这里NSURLSession代理回调线程的最大并发数为 1,是为了使代理方法回调都在一个串行队列中,只有这样才能保证代理方法的回调顺序。因为NSURLSession代理方法回调是异步的,所以这里采用了“异步+串行队列”方式处理。

getTasksWithCompletionHandler:用于异步获取 session 中所有未完成的 task,按理说在初始化中调用这个方法应该不会有 task,但存在这个 issue。所以这里的处理是为了防止 app 从后台回来重新初始化 session 时,一些之前的后台请求导致的 crash

- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
    ...
    self.operationQueue = [[NSOperationQueue alloc] init];
    self.operationQueue.maxConcurrentOperationCount = 1; // 最大并发数为1
    ...
    [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
        for (NSURLSessionDataTask *task in dataTasks) {
            [self addDelegateForDataTask:task uploadProgress:nil downloadProgress:nil completionHandler:nil];
        }
        for (NSURLSessionUploadTask *uploadTask in uploadTasks) {
            [self addDelegateForUploadTask:uploadTask progress:nil completionHandler:nil];
        }
        for (NSURLSessionDownloadTask *downloadTask in downloadTasks) {
            [self addDelegateForDownloadTask:downloadTask progress:nil destination:nil completionHandler:nil];
        }
    }];
  ...
}

3. NSURLRequest 的创建

AFHTTPSessionManager初始化完成后,会调用各种网络请求方法发起请求:

- GET:parameters:success:failure:
- GET:parameters:progress:success:failure:
- HEAD:parameters:success:failure:
- POST:parameters:success:failure:
- POST:parameters:progress:success:failure:
- POST:parameters:constructingBodyWithBlock:success:failure:
- POST:parameters:constructingBodyWithBlock:progress:success:failure:
- PUT:parameters:success:failure:
- PATCH:parameters:success:failure:
- DELETE:parameters:success:failure:

这里所有的网络请求,最终会落到这两个方法上:

  • - dataTaskWithHTTPMethod:URLString:parameters:uploadProgress:downloadProgress:success:failure:
  • - POST:parameters:constructingBodyWithBlock:progress:success:failure:

拆分为两个方式是因为在数据上传时,可以采用表单拼接的方式,这种方式与通用的网络请求有所差异,所以需要剥离出来。

我们先看第一个方法,该方法完成了两件事情:创建NSURLRequestNSURLSessionDataTask

- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
                                       URLString:(NSString *)URLString
                                      parameters:(id)parameters
                                  uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress
                                downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress
                                         success:(void (^)(NSURLSessionDataTask *, id))success
                                         failure:(void (^)(NSURLSessionDataTask *, NSError *))failure
{
    // 通过 requestSerializer 创建 NSMutableURLRequest
    NSError *serializationError = nil;
    NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
    ...
    // 创建 task
    __block NSURLSessionDataTask *dataTask = nil;
    dataTask = [self dataTaskWithRequest:request
                          uploadProgress:uploadProgress
                        downloadProgress:downloadProgress
                       completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
        ...
    }];
    return dataTask;
}

NSURLRequest的创建是由AFHTTPRequestSerializer完成,其继承体系:

最上层的AFURLRequestSerialization为接口,定义的是序列化对象中最重要的用于创建NSURLRequest的方法。基于该协议,AFNetworking 提供了 3 种不同数据形式的序列化类。

  • AFHTTPRequestSerializer: 定义了一些通用方法,参数格式是通用的字典、数组等。实际上我们在进行网络请求时,参数都会封装在字典中,所以该类使用的最多。
  • AFJSONRequestSerializer: 参数格式是 json
  • AFPropertyListRequestSerializer: 参数格式是 plist

AFHTTPRequestSerializer 初始化方法完成了请求头 User-Agent 的配置。同时AFHTTPRequestSerializer中定义了一些NSURLRequest属性,并对这些属性设置了 KVO,当对属性进行修改时,会记录哪些属性修改过,并在创建NSURLRequest时统一设置到NSURLRequest中,其中主要包括请求配置属性。

- (instancetype)init {
    ...
    for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
        if ([self respondsToSelector:NSSelectorFromString(keyPath)]) {
            [self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:AFHTTPRequestSerializerObserverContext];
        }
    }
    ...
}
// 需要进行监听的属性,这些属性都设置到 NSURLRequest 中去
static NSArray * AFHTTPRequestSerializerObservedKeyPaths() {
    static NSArray *_AFHTTPRequestSerializerObservedKeyPaths = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _AFHTTPRequestSerializerObservedKeyPaths = @[
            NSStringFromSelector(@selector(allowsCellularAccess)),
            NSStringFromSelector(@selector(cachePolicy)),
            NSStringFromSelector(@selector(HTTPShouldHandleCookies)),
            NSStringFromSelector(@selector(HTTPShouldUsePipelining)),
            NSStringFromSelector(@selector(networkServiceType)),
            NSStringFromSelector(@selector(timeoutInterval))];
    });
    return _AFHTTPRequestSerializerObservedKeyPaths;
}

创建NSURLRequest的重要函数是requestWithMethod:URLString:parameters:error:,它完成了NSURLRequest的创建、NSURLRequest相关属性设置、HTTP MethodHTTP Header 的设置、参数序列化等工作。

- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
                                 URLString:(NSString *)URLString
                                parameters:(id)parameters
                                     error:(NSError *__autoreleasing *)error
{
    NSURL *url = [NSURL URLWithString:URLString];
    NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
    // 设置请求方法
    mutableRequest.HTTPMethod = method;
    // 设置AFHTTPRequestSerializer对象修改过的属性
    for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
        if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
            [mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
        }
    }
    // 完成参数序列化,并设置到 mutableRequest 中
    mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];
    return mutableRequest;
}

requestBySerializingRequest:withParameters:error: 首先为请求设置部分请求头信息,包括 User-AgentAuthorization,接着完成请求中 query 部分的参数拼接,最后判断当前请求是否是默认设置的 GETHEADDELETE,如果是则将 query 部分添加到请求 url 上,否则放在请求 body 中。

- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
                               withParameters:(id)parameters
                                        error:(NSError *__autoreleasing *)error
{
    // User-Agent 和 Authorization 设置
    NSMutableURLRequest *mutableRequest = [request mutableCopy];
    [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
        if (![request valueForHTTPHeaderField:field]) {
            [mutableRequest setValue:value forHTTPHeaderField:field];
        }
    }];
    ...

    if (self.queryStringSerialization) {
        // 自定义参数拼接
        query = self.queryStringSerialization(request, parameters, &serializationError);
        ...
    } else {
        switch (self.queryStringSerializationStyle) {
            case AFHTTPRequestQueryStringDefaultStyle:
                // 默认参数拼接
                query = AFQueryStringFromParameters(parameters);
                break;
        }
    }

    if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
        if (query && query.length > 0) {
            // 将参数设置到 url 中
            mutableRequest.URL = [NSURL URLWithString:[[mutableRequest.URL absoluteString] stringByAppendingFormat:mutableRequest.URL.query ? @"&%@" : @"?%@", query]];
        }
    } else {
        ...
        // 将参数设置到 http body 中
        [mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]];
    }
    return mutableRequest;
}

4. NSURLSessionDataTask 的创建

NSURLRequest创建完成后,就需要创建 tasktask 的创建主要涉及dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:task 可以直接采用NSURLSessiondataTaskWithRequest:接口即可,所以该函数主要负责处理请求过程中的回调设置。

- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
                               uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
                             downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
                            completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject,  NSError * _Nullable error))completionHandler {
    __block NSURLSessionDataTask *dataTask = nil;
    // 这里是为了解决iOS8以下task会在异步创建的问题
    url_session_manager_create_task_safely(^{
        dataTask = [self.session dataTaskWithRequest:request];
    });
    //  设置请求回调
    [self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
    return dataTask;
}

5. 请求回调的处理

NSURLSession中涉及的网络代理较多,包括:

  • NSURLSessionDelegate :所有代理的基类,定义了网络请求最基础的代理方法。
  • NSURLSessionTaskDelegate :定义了网络请求任务相关的代理方法。
  • NSURLSessionDownloadDelegate :用于下载任务相关的代理方法,比如下载进度等等。
  • NSURLSessionDataDelegate :用于普通数据任务和上传任务。

AFNetworking 中机会对几乎所有代理方法进行了处理:

1. NSURLSessionDelegate
- URLSession:didBecomeInvalidWithError:
- URLSession:didReceiveChallenge:completionHandler:
- URLSessionDidFinishEventsForBackgroundURLSession: // 后台下载相关

2. NSURLSessionTaskDelegate
- URLSession:task:didCompleteWithError:
- URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:
- URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:
- URLSession:task:needNewBodyStream:
- URLSession:task:didReceiveChallenge:completionHandler:`
/// 未实现的方法:
- URLSession:task:willBeginDelayedRequest:completionHandler:
- URLSession:taskIsWaitingForConnectivity:
- URLSession:task:didFinishCollectingMetrics:

3. NSURLSessionDataDelegate
- URLSession:dataTask:didReceiveResponse:completionHandler:
- URLSession:dataTask:didBecomeDownloadTask:
- URLSession:dataTask:didReceiveData:
- URLSession:dataTask:willCacheResponse:completionHandler:
/// 未实现的方法:
- URLSession:dataTask:didBecomeStreamTask:

4. NSURLSessionDownloadDelegate
- URLSession:downloadTask:didFinishDownloadingToURL:
- URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:
- URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:

上述回调方法的处理均是在AFURLSessionManager中完成的。为了功能的划分 AFNetworking 将与请求状态相关的代理分发到了AFURLSessionManagerTaskDelegate中,包括上传/下载进度和请求完成的回调。

// NSURLSessionTaskDelegate 网络请求任务相关的代理方法
- URLSession:task:didCompleteWithError:
- URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:
// NSURLSessionDataDelegate 普通数据任务和上传任务代理方法
- URLSession:dataTask:didReceiveData:
// NSURLSessionDownloadDelegate 下载任务相关的代理方法
- URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:
- URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:
- URLSession:downloadTask:didFinishDownloadingToURL:

所以代理处理的整体结构如图:

设置代理的方法由addDelegateForDataTask:uploadProgress:downloadProgress:completionHandler:完成,该方法设置了上传/下载进度和请求完成的回调。并将delegatetask的对应关系保存在字典mutableTaskDelegatesKeyedByTaskIdentifier中。在AFURLSessionManager中网络回调触发时,通过task获取对应的delegate,然后处理或调整delegate中相关内容。

- (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask
                uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
              downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
             completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
    AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:dataTask];
    delegate.manager = self;
    delegate.completionHandler = completionHandler;
    delegate.uploadProgressBlock = uploadProgressBlock;
    delegate.downloadProgressBlock = downloadProgressBlock;

    dataTask.taskDescription = self.taskDescriptionForSessionTasks;

    // 保存 delegate 和 task 间的映射关系
    [self setDelegate:delegate forTask:dataTask];
}

AFURLSessionManagerTaskDelegate声明的属性中除了包含各种回调,还包括两个NSProgress,它们分别用于处理上传和下载进度。我们重点关注URLSession:task:didCompleteWithError:回调,其他回调主要处理上传/下载 block 。从代码中我们可以看出该回调中主要处理这几件事情:

  1. 收集请求完成时通知相关信息;
  2. 通过responseSerializer处理响应数据;
  3. 执行completionHandler回调;
  4. 发送请求完成的通知AFNetworkingTaskDidCompleteNotification;

这里有几个问题需要注意。

  1. 3、4 是在dispatch_group_async中执行的,同时其 groupqueue 均可以通过业务方设置,这就说明这里的 dispatch_group 主要是提供给业务方使用的,如果单纯的从回调执行来看,则不需要通过 dispatch_group 处理;
  2. 通知的发送。通知的接收线程和发起线程是同一个线程,所以这里采用了 dispatch 到主线程的方式完成。当然也可以通过NSMachPort进行处理。
- (void)URLSession:(__unused NSURLSession *)session
              task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
    __strong AFURLSessionManager *manager = self.manager;
    __block id responseObject = nil;
    // 1. 收集通知信息
    __block NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
    userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer;
    ...

    if (error) {
        userInfo[AFNetworkingTaskDidCompleteErrorKey] = error;

        dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
            // 3. 执行`completionHandler`回调;
            if (self.completionHandler) {
                self.completionHandler(task.response, responseObject, error);
            }
            // 4. 发送请求完成的通知
            dispatch_async(dispatch_get_main_queue(), ^{
                [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
            });
        });
    } else {
        dispatch_async(url_session_manager_processing_queue(), ^{
            NSError *serializationError = nil;
            // 2. 通过`responseSerializer`处理响应数据
            responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError];
            ...

            dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
                // 3. 执行`completionHandler`回调;
                if (self.completionHandler) {
                    self.completionHandler(task.response, responseObject, serializationError);
                }
                // 4. 发送请求完成的通知
                dispatch_async(dispatch_get_main_queue(), ^{
                    [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
                });
            });
        });
    }
}

响应数据的反序列化由AFHTTPResponseSerializer完成,该类实现了AFURLResponseSerialization协议,协议仅有一个函数,函数功能包括:解析返回数据、检查数据是否合法、解析服务器返回的NSData对象。

AFHTTPResponseSerializer并有多个子类:

  • AFJSONResponseSerializer: 采用NSJSONSerialization解析 json 格式;
  • AFXMLParserResponseSerializer: 采用NSXMLParser解析 xml 格式;
  • AFXMLDocumentResponseSerializer: 采用NSXMLDocument解析 xml 格式;
  • AFPropertyListResponseSerializer: 解析 plist 格式;
  • AFImageResponseSerializer: 解析图片;
  • AFCompoundResponseSerializer: 组合解析器,可以将多个解析器组合起来,以同时支持多种格式的数据解析;

AFHTTPResponseSerializer作为基类,提供了基础的验证数据是否合法的方法validateResponse:data:error:。从头文件中定义的属性acceptableStatusCodesacceptableContentTypes,可以猜测验证的是 accept-typecode 的合法性,实际也是如此。AFHTTPResponseSerializer的子类负责设置对应的 accept-typecode 则在基类中处理,仅接受 2XXcode

- (BOOL)validateResponse:(NSHTTPURLResponse *)response
                    data:(NSData *)data
                   error:(NSError * __autoreleasing *)error
{
    BOOL responseIsValid = YES;
    NSError *validationError = nil;
    if (response && [response isKindOfClass:[NSHTTPURLResponse class]]) {
        // 处理 accept-type 类型错误
        if (self.acceptableContentTypes && ![self.acceptableContentTypes containsObject:[response MIMEType]] &&
            !([response MIMEType] == nil && [data length] == 0)) {
            if ([data length] > 0 && [response URL]) {
                NSMutableDictionary *mutableUserInfo = [@{
                                                          NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: unacceptable content-type: %@", @"AFNetworking", nil), [response MIMEType]],
                                                          NSURLErrorFailingURLErrorKey:[response URL],
                                                          AFNetworkingOperationFailingURLResponseErrorKey: response,
                                                        } mutableCopy];
                if (data) {
                    mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data;
                }
                validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:mutableUserInfo], validationError);
            }
            responseIsValid = NO;
        }
        // 处理响应 code 错误
        if (self.acceptableStatusCodes && ![self.acceptableStatusCodes containsIndex:(NSUInteger)response.statusCode] && [response URL]) {
            NSMutableDictionary *mutableUserInfo = [@{
                                               NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: %@ (%ld)", @"AFNetworking", nil), [NSHTTPURLResponse localizedStringForStatusCode:response.statusCode], (long)response.statusCode],
                                               NSURLErrorFailingURLErrorKey:[response URL],
                                               AFNetworkingOperationFailingURLResponseErrorKey: response,
                                       } mutableCopy];
            if (data) {
                mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data;
            }
            validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo], validationError);
            responseIsValid = NO;
        }
    }
    if (error && !responseIsValid) {
        *error = validationError;
    }
    return responseIsValid;
}

6. 小结

至此,通过 AFNetworking 发起网络请求的整体流程就梳理完了。网络的流程是:

  1. 创建AFHTTPSessionManager
  2. 设置请求参数和回调,调用AFHTTPSessionManager方法发起请求;

从本文的分析可以看出,采用 AFNetworkingNSURLSession 发起网络请求的区别就很明显了:

  1. 对通用的网络回调,用 block 替代 delegate 对网络回调进行处理;对非通用的网络回调通过 NSNotification 的方式发送到业务层;
  2. AFNetworking 帮我们封装了请求参数序列化和响应参数反序列化相关的类,我们可以直接使用,也可自定义;

当然还有一些其他功能,比如 HTTPS 请求证书的验证,multipart 请求数据的拼接,网络状态的监听等会再接下来的内容中介绍。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345