AFNetworking3.0学习笔记[更新中]

来源:互联网 发布:linux 8080端口被占用 编辑:程序博客网 时间:2024/05/23 21:50

9th,August,2016

概述

github地址
Github是这样介绍的:A delightful networking framework for iOS, OS X, watchOS, and tvOS。也就是说这是一款非常友好的第三方库,且适用于iOS,OS X,watchOS和tvOS平台。模块化结构,丰富的API,一款非常优秀的网络。
AFNetworking3.x的认识: 就是基于NSURLSession进行高度封装,给开发者提供更为友好的接口。
查看资料发现AFNetworking2.0与3.0区别还是蛮大的,iOS9.0中已经弃用NSURLConnection。因而AFNetworking3.0中没有对NSURLConnection的支持,而是基于NSURLSession进行封装。(ps: AFNetworking2.0则是基于NSURLConnection与NSOperation开发) 如果需从AFNetworking2.0迁移到3.0可以查看Github:AFNetworking 3.0 Migration Guide

AFNetworking目录结构
1. AFHTTPSessionManager // 基于HTTP协议的会话管理器,继承AFURLSessionManager
2. AFNetworking
3. AFNetworkReachabilityManager // 网络可达性
4. AFSecurityPolicy // 安全性
5. AFURLRequestSerialization // 请求数据序列化
6. AFURLResponseSerialization // 响应数据序列化
7. AFURLSessionManager// 会话管理器,核心类

架构理解

这里写图片描述
from Draveness

苹果原生网络解决方案

在分析AFNetworking之前,先总结下苹果原生的网络解决方案。2003年跟着Safari一起推出的网络API:NSURLConnection,之后苹果重构了NSURLConnection,在2013年iOS7引入NSURLSession取代NSURLConnection,2015年iOS9弃用NSURLConnection。

NSURLConnection

从2003年开始,这套使用了十来年的API,虽然因代码简洁性一直被诟病。但在可扩展性与可组合性等方面表现的还是非常优秀的。NSURLConnection不单单只NSURLConnection这个组件,而是指一套构成URL加载系统的相互关联的组件:NSURLConnection,NSURLRequest,NSURLResponse,NSURLProtocol,NSURLCache,NSHTTPCookieStorage,NSURLCredentialStorage(证书存储)。
这里写图片描述

NSURLSession

同NSURLConnection,NSURLSession也是一组相互关联的组件:除了大部分与NSURLConnection相同的组件。NSURLSession类也是做了比较大的调整。NSURLConnection被调整为由NSURLSession,NSURLSessionConfiguration,以及NSURLSessionTask系列(包括NSURLSessionDataTask,NSURLSessionUploadTask,NSURLSessionDownloadTask以及iOS9推出的NSURLStreamTask)。
NSURLSession基本结构

缓存策略

cachePolicy:
1、NSURLRequestUseProtocolCachePolicy 协议缓存,根据 response 中的 Cache-Control 字段判断缓存是否有效,如果缓存有效则使用缓存数据否则重新从服务器请求
2、NSURLRequestReloadIgnoringLocalCacheData 不使用缓存,直接请求新数据
3、NSURLRequestReloadIgnoringCacheData 等同于 NSURLRequestReloadIgnoringLocalCacheData
4、NSURLRequestReturnCacheDataElseLoad 直接使用缓存数据不管是否有效,没有缓存则重新请求
5、NSURLRequestReturnCacheDataDontLoad 直接使用缓存数据不管是否有效,没有缓存数据则失败

会话

NSURLSession 支持进程三种会话:
1、defaultSessionConfiguration:进程内会话(默认会话),用硬盘来缓存数据。
2、ephemeralSessionConfiguration:临时的进程内会话(内存),不会将 cookie、缓存储存到本地,只会放到内存中,当应用程序退出后数据也会消。
3、backgroundSessionConfiguration:后台会话,相比默认会话,该会话会在后台开启一个线程进行网络数据处理。

对比

苹果重构后的NSURLSession对开发者还是相对友好,也很简洁。如果没有特别依赖第三方库(AFNetworking)提供的功能,出于不需要依赖任何第三方库的考虑(依赖第三方库的成本还是有的),推荐使用原生API。

开始看源码啦

AFHTTPSessionManager

[[AFHTTPSessionManager alloc] initWithBaseURL:[[NSURL alloc] initWithString: TEST_URL]]

先看看调用栈,

// 从Draveness的github源码分析复制过来的- [AFHTTPSessionManager initWithBaseURL:]    - [AFHTTPSessionManager initWithBaseURL:sessionConfiguration:]        - [AFURLSessionManager initWithSessionConfiguration:]            - [NSURLSession sessionWithConfiguration:delegate:delegateQueue:]            - [AFJSONResponseSerializer serializer] // 负责序列化响应            - [AFSecurityPolicy defaultPolicy] // 负责身份认证            - [AFNetworkReachabilityManager sharedManager] // 查看网络连接情况        - [AFHTTPRequestSerializer serializer] // 负责序列化请求        - [AFJSONResponseSerializer serializer] // 负责序列化响应

初始化都做了哪些工作:
1. session会话配置初始化 // AFHTTPSessionManager
2. 序列化响应初始化 // AFRequestSerializer
3. 身份认证初始化,保证安全性 // AFSecurityPolicy
4. 网络连接情况初始化 // AFNetworkReachabilityManager
5. 序列化响应初始化 // AFResponseSerializer

发送请求

  1. 通过请求序列化获取请求NSMutableURLRequest;
  2. 调用父类方法dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler获取任务实例,并且通过response设置成功和失败的block
- (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{    NSError *serializationError = nil;    NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];    if (serializationError) {        if (failure) {            dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{                failure(nil, serializationError);            });        }        return nil;    }    __block NSURLSessionDataTask *dataTask = nil;    dataTask = [self dataTaskWithRequest:request                          uploadProgress:uploadProgress                        downloadProgress:downloadProgress                       completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {        if (error) {            if (failure) {                failure(dataTask, error);            }        } else {            if (success) {                success(dataTask, responseObject);            }        }    }];    return dataTask;}

AFURLSessionManager

初始化方法

  1. NSURLSessionConfiguration默认为default类型,通过NSOperationQueue初始化设置操作队列,通过设置maxConcurrentOperationCount为1将操作队列设置成同步队列,初始化会话session时设置配置,代理,操作队列。
    初始化响应序列化,安全策略。
  2. mutableTaskDelegateKeyedByTaskIdentifier字典初始化。这个字典时用来让每个task跟对应的代理建立映射。ps: AF对任务的代理进行了封装,转发给AF自定义的代理
  3. getTasksWithCompletionHandler是NSURLSession自带的方法,用来异步获取当前没有完成的任务。初始化时参数中的数组为空,但如果是从后台回到前台,则数组就不一定为空了。
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {    self = [super init];    if (!self) {        return nil;    }    if (!configuration) {        configuration = [NSURLSessionConfiguration defaultSessionConfiguration];    }    self.sessionConfiguration = configuration;    self.operationQueue = [[NSOperationQueue alloc] init];    self.operationQueue.maxConcurrentOperationCount = 1;    self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];    self.responseSerializer = [AFJSONResponseSerializer serializer];    self.securityPolicy = [AFSecurityPolicy defaultPolicy];#if !TARGET_OS_WATCH    self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];#endif    self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];    self.lock = [[NSLock alloc] init];    self.lock.name = AFURLSessionManagerLockName;    [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];        }    }];    return self;}

获取任务实例

  1. 通过请求创建任务实例dataTask。dataTask是在url_session_manager_create_task_safely这个block中创建,目的是为了适配iOS8以下,创建session时,偶发出现session属性taskIdentifier值不唯一的情况。
  2. addDelegateForDataTask给该任务实例添加数据代理。
- (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;    url_session_manager_create_task_safely(^{        dataTask = [self.session dataTaskWithRequest:request];    });    [self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];    return dataTask;}

原生代理转发

didCompleteWithError

- (void)URLSession:(NSURLSession *)session              task:(NSURLSessionTask *)taskdidCompleteWithError:(NSError *)error{    AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];    // delegate may be nil when completing a task in the background    if (delegate) {        [delegate URLSession:session task:task didCompleteWithError:error];        [self removeDelegateForTask:task];    }    if (self.taskDidComplete) {        self.taskDidComplete(session, task, error);    }}

didReceiveData

- (void)URLSession:(NSURLSession *)session          dataTask:(NSURLSessionDataTask *)dataTask    didReceiveData:(NSData *)data{    AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask];    [delegate URLSession:session dataTask:dataTask didReceiveData:data];    if (self.dataTaskDidReceiveData) {        self.dataTaskDidReceiveData(session, dataTask, data);    }}

AFURLResponseSerialization

处理响应的模块,将请求返回的数据解析成对应的格式。
首先声明AFURLResponseSerialization是协议,该文件内所有的类都遵循该协议。AFHTTPResponseSerializer为根类,其他(如AFJSONResponseSerializer,AFXMLParserResponseSerializer等)都是继承AFHTTPResponseSerializer。

初始化方法:

- (instancetype)init {    ... # 初始化     self.stringEncoding = NSUTF8StringEncoding;// 设置为UTF8字符编码    self.acceptableStatusCodes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 100)];// 设置有效状态码    self.acceptableContentTypes = nil;// 初始化里未设置接收的内容类型,代表不限制接收内容类型    return self;}

验证响应的有效性

- (BOOL)validateResponse:(NSHTTPURLResponse *)response
data:(NSData *)data
error:(NSError * __autoreleasing *)error

AFURLRequestSerialization

修改请求(主要是 HTTP 请求)的头部,提供了一些语义明确的接口设置 HTTP 头部字段。

获取请求

  1. 通过url实例化NSMutableURLRequest,并设置请求类型。
  2. 遍历AFHTTPRequestSerializerObservedKeyPaths数组(该数组存放的是NSURLReqeust的一些属性),通过mutableObservedChangedKeyPaths集合(初始化时会对该属性进行重置并添加对AFHTTPRequestSerializerObservedKeyPaths中属性的观察,一旦观察到值被设置就添加到mutableObservedChangedKeyPaths集合中)中如果存在该键值,则将属性设置给request。
  3. 通过调用requestBySerializingRequest方法将参数添加到原有请求上返回一个对参数进行编码的新请求。
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method                                 URLString:(NSString *)URLString                                parameters:(id)parameters                                     error:(NSError *__autoreleasing *)error{    NSParameterAssert(method);    NSParameterAssert(URLString);    NSURL *url = [NSURL URLWithString:URLString];    NSParameterAssert(url);    NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];    mutableRequest.HTTPMethod = method;    for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {        if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {            [mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];        }    }    mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];    return mutableRequest;}

对参数进行编码

  1. HTTPRequestHeaders字典中进行遍历,如果旧请求中未设置该header的值,则在新请求中设置该header。
  2. 将参数从数组/字典/集合转化为字符串,具体的转化方式可以自定义(设置了queryStringSerialization属性),也可以使用AFNetworking默认的方式。
  3. 根据请求类型,将参数设置到请求中。
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request                               withParameters:(id)parameters                                        error:(NSError *__autoreleasing *)error{    NSParameterAssert(request);    NSMutableURLRequest *mutableRequest = [request mutableCopy];    [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {        if (![request valueForHTTPHeaderField:field]) {            [mutableRequest setValue:value forHTTPHeaderField:field];        }    }];    NSString *query = nil;    if (parameters) {        if (self.queryStringSerialization) {            NSError *serializationError;            query = self.queryStringSerialization(request, parameters, &serializationError);            if (serializationError) {                if (error) {                    *error = serializationError;                }                return nil;            }        } else {            switch (self.queryStringSerializationStyle) {                case AFHTTPRequestQueryStringDefaultStyle:                    query = AFQueryStringFromParameters(parameters);                    break;            }        }    }    if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {        if (query && query.length > 0) {            mutableRequest.URL = [NSURL URLWithString:[[mutableRequest.URL absoluteString] stringByAppendingFormat:mutableRequest.URL.query ? @"&%@" : @"?%@", query]];        }    } else {        // #2864: an empty string is a valid x-www-form-urlencoded payload        if (!query) {            query = @"";        }        if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {            [mutableRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];        }        [mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]];    }    return mutableRequest;}

AFNetworkingReachabilityManager

监测网络的可达性,基于SCNetworkReachabilityRef类进行开发。
类中的私有方法可以写成static void functionName() {} 这种C函数格式。
keyPathsForValuesAffectingValueForKey // 注册键值依赖
参考:AFNetworking 3.0 源码解读(一)之 AFNetworkReachabilityManager

AFSecurityPolicy

在发起HTTPS连接时,验证证书是否正确。

typedef NS_ENUM(NSUInteger, AFSSLPinningMode) {    AFSSLPinningModeNone,// 无条件信任服务器证书    AFSSLPinningModePublicKey,// 对服务器返回的证书进行公钥验签    AFSSLPinningModeCertificate,// 服务器返回的证书与本地证书进行比对校验};

核心方法

- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust                  forDomain:(nullable NSString *)domain;

小结

  1. AFNetworking1.0基于NSURLConnection封装。AFNetworking2.0提供了基于NSURLConnection的API与基于NSURLSession的API,而在AFNetworking3.0中由于苹果在iOS9.0弃用NSURLConnection,所以相应的在3.0中也移除了对NSURLConnection的支持。 AFNetworking2.0依赖NSURLConnection和NSOperation,AFNetworking3.0依赖NSURLSession.

ZA

Tip

  1. 初始化AFURLSessionManager或者AFHTTPSessionManager时,如果NSURLSessionConfiguration为Default类型,则无需再进行配置,因为框架中默认就是设置为defaultSessionConfiguration。

报错

  1. 报错:“module ‘AFNetworking’ not found“
    解决方法:User Header Search Paths is set to $(SRCROOT)/Pods and is recursive
    from : stackoverflow-use_frameworks! and Model not found build error

参考资料

AFNetworking源码解读资料

  1. Draveness源码分析
    通过简单的调用AFNetworking发送请求的代码,查看调用栈作为入口了解整个框架的架构。
    并分析AFURLSessionManager,AFURLSerialization,AFNetworkReachabilityManager以及验证HTTPS证书。了解每个部分是如何工作以及各个部分又是如何有机组合在一起。
  2. Bang源码分析
    Bang的源码侧重于细节,分析了其中使用的一些hack等。
  3. 马在路上源码分析

  4. itangqi源码分析
    在分析一种介绍了AFNetworking2.0与AFNetworking3.0的区别,大体上的资料来自于github/AFNetworking提供的2.0到3.0的迁移指南。二中正式开始分析,也是从整体框架开始分析,有点像Draveness的思路。

  5. 涂耀辉源码解读

其他相关参考资料

AFNetworking 概述(一)
AFNetworking 2.0-发布于2013年9月16日
AFNetworking 3.0 源码阅读笔记(一)
AFNetworking 3.0 Migration Guide
浅析 iOS 应用网络层设计
NSURLSession 网络库 - 原生系统送给我们的礼物

log:
26th,March,2017

0 0
原创粉丝点击