共计 22554 个字符,预计需要花费 57 分钟才能阅读完成。
AFN
Why AFN
大多项目中我们都会使用网络请求去和服务端进行交互,而对于 iOS 开发者而言,最广为人知的网络请求框架莫过于 AFNetworking 了,那么大家有没有想过为什么广大的开发者选择了它,它对比 iOS 原生的网络请求有什么区别呢,下面我会总结下自己对于 AFN 的看法和体会,也希望能对一样学习的小伙伴提供一些帮助
AFN 解析
基本使用
GET
- (void)sendRequest {AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
NSURLSessionDataTask *task = [manager GET:@"http://httpbin.org/get" parameters:@{@"arg1":@(100), @"arg2": @{@"foo":@"bar"}} progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {}];
}
POST
- (void)sendRequest {AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
NSURLSessionDataTask *task = [manager POST:@"http://httpbin.org/post" parameters:@{@"arg1":@(100), @"arg2": @{@"foo":@"bar"}} progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {}];
}
这是最简单的使用方法,下面我们一层一层分析 AFN 是怎样处理一个请求的
接口部分 AFHTTPSessionManager & AFURLSessionManager
AFHTTPSessionManager
+ (instancetype)manager {return [[[self class] alloc] initWithBaseURL:nil];
}
- (instancetype)init {return [self initWithBaseURL:nil];
}
- (instancetype)initWithBaseURL:(NSURL *)url {return [self initWithBaseURL:url sessionConfiguration:nil];
}
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {return [self initWithBaseURL:nil sessionConfiguration:configuration];
}
- (instancetype)initWithBaseURL:(NSURL *)url
sessionConfiguration:(NSURLSessionConfiguration *)configuration
{self = [super initWithSessionConfiguration:configuration];
if (!self) {return nil;}
// 这里为 baseURL 做处理,如果传入的 URL 没有加最后的斜杠这里会补上
if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) {url = [url URLByAppendingPathComponent:@""];
}
self.baseURL = url;
// 这里初始化了请求和响应的序列化对象,默认使用 json 作为响应的序列化方式
self.requestSerializer = [AFHTTPRequestSerializer serializer];
self.responseSerializer = [AFJSONResponseSerializer serializer];
return self;
}
由于 AFHTTPSessionManager 是继承于 AFURLSessionManager,我们接下来看下AFURLSessionManager 的初始化方法
AFURLSessionManager
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {self = [super init];
if (!self) {return nil;}
// 如果没有传入 NSURLSessionConfiguration 则初始化为 defaultSessionConfiguration
if (!configuration) {configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
}
self.sessionConfiguration = configuration;
// 初始化网络请求回调的队列
self.operationQueue = [[NSOperationQueue alloc] init];
self.operationQueue.maxConcurrentOperationCount = 1;
// 初始化 NSURLSession
self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
self.responseSerializer = [AFJSONResponseSerializer serializer];
// 默认安全策略 AFSSLPinningModeNone
self.securityPolicy = [AFSecurityPolicy defaultPolicy];
#if !TARGET_OS_WATCH
self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
#endif
// 初始化任务代理的 Map
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;
}
说完了初始化我们来看具体的一个请求发送
- (NSURLSessionDataTask *)GET:(NSString *)URLString
parameters:(id)parameters
success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
{return [self GET:URLString parameters:parameters progress:nil success:success failure:failure];
}
最终会进入这个方法
- (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;
// 由 requestSerializer 负责具体请求的解析 ,requestSerializer 部分下面会说
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
// 如果请求解析失败返回 nil,直接通过 block 返回
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;
}
再之后会进入这个方法
- (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;
}
这里 url_session_manager_create_task_safely 解决了 iOS8.0 之前并行创建出来的 task 的 taskIdentifier 不唯一的 BUG,实现起来就是使用 GCD 创建了一个串行队列进行 task 的操作,如下所示
static dispatch_queue_t url_session_manager_creation_queue() {
static dispatch_queue_t af_url_session_manager_creation_queue;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{af_url_session_manager_creation_queue = dispatch_queue_create("com.alamofire.networking.session.manager.creation", DISPATCH_QUEUE_SERIAL);
});
return af_url_session_manager_creation_queue;
}
static void url_session_manager_create_task_safely(dispatch_block_t block) {if (NSFoundationVersionNumber < NSFoundationVersionNumber_With_Fixed_5871104061079552_bug) {
// Fix of bug
// Open Radar:http://openradar.appspot.com/radar?id=5871104061079552 (status: Fixed in iOS8)
// Issue about:https://github.com/AFNetworking/AFNetworking/issues/2093
dispatch_sync(url_session_manager_creation_queue(), block);
} else {block();
}
}
接下来继续查看上面的为 task 添加代理的方法 addDelegateForDataTask
- (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;
dataTask.taskDescription = self.taskDescriptionForSessionTasks;
// 将代理与对象关联,存进 Map
[self setDelegate:delegate forTask:dataTask];
delegate.uploadProgressBlock = uploadProgressBlock;
delegate.downloadProgressBlock = downloadProgressBlock;
}
由于对字典的操作不是线程安全的,所以使用 NSLock 加锁
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
forTask:(NSURLSessionTask *)task
{NSParameterAssert(task);
NSParameterAssert(delegate);
[self.lock lock];
self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
[self addNotificationObserverForTask:task];
[self.lock unlock];
}
添加监听
- (void)addNotificationObserverForTask:(NSURLSessionTask *)task {[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidResume:) name:AFNSURLSessionTaskDidResumeNotification object:task];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidSuspend:) name:AFNSURLSessionTaskDidSuspendNotification object:task];
}
在监听到状态变化时向外部发出通知,方便外部处理
- (void)taskDidResume:(NSNotification *)notification {
NSURLSessionTask *task = notification.object;
if ([task respondsToSelector:@selector(taskDescription)]) {if ([task.taskDescription isEqualToString:self.taskDescriptionForSessionTasks]) {dispatch_async(dispatch_get_main_queue(), ^{[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidResumeNotification object:task];
});
}
}
}
- (void)taskDidSuspend:(NSNotification *)notification {
NSURLSessionTask *task = notification.object;
if ([task respondsToSelector:@selector(taskDescription)]) {if ([task.taskDescription isEqualToString:self.taskDescriptionForSessionTasks]) {dispatch_async(dispatch_get_main_queue(), ^{[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidSuspendNotification object:task];
});
}
}
}
下面主要看 AFURLSessionManagerTaskDelegate 实现的这个方法
#pragma mark - NSURLSessionTaskDelegate
- (void)URLSession:(__unused NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
// 取出 AFURLSessionManager
__strong AFURLSessionManager *manager = self.manager;
__block id responseObject = nil;
__block NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer;
// 当请求完成后释放内存
NSData *data = nil;
if (self.mutableData) {data = [self.mutableData copy];
//We no longer need the reference, so nil it out to gain back some memory.
self.mutableData = nil;
}
if (self.downloadFileURL) {userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL;
} else if (data) {userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = data;
}
if (error) {userInfo[AFNetworkingTaskDidCompleteErrorKey] = error;
// 异步添加到 GCD 任务组,如果没有指定会默认生成一个,回调队列如果没有指定会默认为主队列
dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{if (self.completionHandler) {self.completionHandler(task.response, responseObject, error);
}
// 在主队列中发送 AFNetworkingTaskDidCompleteNotification 通知
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;
responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError];
if (self.downloadFileURL) {responseObject = self.downloadFileURL;}
if (responseObject) {userInfo[AFNetworkingTaskDidCompleteSerializedResponseKey] = responseObject;
}
if (serializationError) {userInfo[AFNetworkingTaskDidCompleteErrorKey] = serializationError;
}
dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{if (self.completionHandler) {self.completionHandler(task.response, responseObject, serializationError);
}
dispatch_async(dispatch_get_main_queue(), ^{[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
});
});
});
}
}
manager 和 taskDelegate 部分先看到这里
序列化部分 AFURLRequestSerialization & AFURLResponseSerialization
先从 AFURLRequestSerialization 开始看起
首先,AFURLRequestSerialization 只是协议,具体实现由遵守协议的类来实现,我们主要看 AFHTTPRequestSerializer 这个类
- (instancetype)init {self = [super init];
if (!self) {return nil;}
// 设置默认字符串编码
self.stringEncoding = NSUTF8StringEncoding;
// 初始化请求头的 Map
self.mutableHTTPRequestHeaders = [NSMutableDictionary dictionary];
// 为头部 Map 的变动创建一个并行队列
self.requestHeaderModificationQueue = dispatch_queue_create("requestHeaderModificationQueue", DISPATCH_QUEUE_CONCURRENT);
// 设置头部的 acceptLanguages
NSMutableArray *acceptLanguagesComponents = [NSMutableArray array];
[[NSLocale preferredLanguages] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {float q = 1.0f - (idx * 0.1f);
[acceptLanguagesComponents addObject:[NSString stringWithFormat:@"%@;q=%0.1g", obj, q]];
*stop = q <= 0.5f;
}];
[self setValue:[acceptLanguagesComponents componentsJoinedByString:@","] forHTTPHeaderField:@"Accept-Language"];
// UA 标识符
NSString *userAgent = nil;
#if TARGET_OS_IOS
// User-Agent Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.43
userAgent = [NSString stringWithFormat:@"%@/%@ (%@; iOS %@; Scale/%0.2f)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[UIDevice currentDevice] model], [[UIDevice currentDevice] systemVersion], [[UIScreen mainScreen] scale]];
#elif TARGET_OS_WATCH
// User-Agent Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.43
userAgent = [NSString stringWithFormat:@"%@/%@ (%@; watchOS %@; Scale/%0.2f)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[WKInterfaceDevice currentDevice] model], [[WKInterfaceDevice currentDevice] systemVersion], [[WKInterfaceDevice currentDevice] screenScale]];
#elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
userAgent = [NSString stringWithFormat:@"%@/%@ (Mac OS X %@)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[NSProcessInfo processInfo] operatingSystemVersionString]];
#endif
if (userAgent) {if (![userAgent canBeConvertedToEncoding:NSASCIIStringEncoding]) {NSMutableString *mutableUserAgent = [userAgent mutableCopy];
if (CFStringTransform((__bridge CFMutableStringRef)(mutableUserAgent), NULL, (__bridge CFStringRef)@"Any-Latin; Latin-ASCII; [:^ASCII:] Remove", false)) {userAgent = mutableUserAgent;}
}
[self setValue:userAgent forHTTPHeaderField:@"User-Agent"];
}
// HTTP Method Definitions; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
// 只有这几种请求 Type 会将参数编码进 URL 中
self.HTTPMethodsEncodingParametersInURI = [NSSet setWithObjects:@"GET", @"HEAD", @"DELETE", nil];
监听 AFHTTPRequestSerializerObservedKeyPaths 包含的 key
self.mutableObservedChangedKeyPaths = [NSMutableSet set];
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {if ([self respondsToSelector:NSSelectorFromString(keyPath)]) {[self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:AFHTTPRequestSerializerObserverContext];
}
}
return self;
}
这里观察的有这些,观察这些也是为了能够在相关的属性被设置的时候可以及时更新
// 允许蜂窝网络访问
// 缓存策略
// 是否响应 setCookie
// http 复用 tcp 连接
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;
}
在获取 header 的时候使用串行队列同步进行获取,保障数据一致性
- (NSDictionary *)HTTPRequestHeaders {
NSDictionary __block *value;
dispatch_sync(self.requestHeaderModificationQueue, ^{value = [NSDictionary dictionaryWithDictionary:self.mutableHTTPRequestHeaders];
});
return value;
}
在设置 header 的时候使用 GCD 的栅栏函数保证顺序设置
- (void)setValue:(NSString *)value
forHTTPHeaderField:(NSString *)field
{
dispatch_barrier_async(self.requestHeaderModificationQueue, ^{[self.mutableHTTPRequestHeaders setValue:value forKey:field];
});
}
在请求序列化的时候还贴心的为我们实现了 HTTP BASIC 认证
- (void)setAuthorizationHeaderFieldWithUsername:(NSString *)username
password:(NSString *)password
{NSData *basicAuthCredentials = [[NSString stringWithFormat:@"%@:%@", username, password] dataUsingEncoding:NSUTF8StringEncoding];
NSString *base64AuthCredentials = [basicAuthCredentials base64EncodedStringWithOptions:(NSDataBase64EncodingOptions)0];
[self setValue:[NSString stringWithFormat:@"Basic %@", base64AuthCredentials] forHTTPHeaderField:@"Authorization"];
}
下面具体看初始化 Request 的阶段
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(id)parameters
error:(NSError *__autoreleasing *)error
{NSParameterAssert(method);
NSParameterAssert(URLString);
// 生成 url
NSURL *url = [NSURL URLWithString:URLString];
NSParameterAssert(url);
// 生成 request 并设置 http 方法
NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
mutableRequest.HTTPMethod = method;
// 取出观察的对象,使用 KVC 对 request 进行设置
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;
}
下面还有进一步的解析
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
withParameters:(id)parameters
error:(NSError *__autoreleasing *)error
{NSParameterAssert(request);
NSMutableURLRequest *mutableRequest = [request mutableCopy];
// 设置 Header
[self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {if (![request valueForHTTPHeaderField:field]) {[mutableRequest setValue:value forHTTPHeaderField:field];
}
}];
// 传入的参数会进行解析,如果没有传入解析 handler 会使用默认的
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;
}
}
}
// 如果请求的类型是(GET,HEAD,DELETE)就将参数编码进 URL 中
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 {
// 这里是将参数放入 Body 中,如果没有参数传入就默认传入空字符串,生成 payload
if (!query) {query = @"";}
// 这里的请求是排除了(GET,HEAD,DELETE)的方式,因此没有写入 content-type 的话会默认设置 header 为表单提交方式
if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {[mutableRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
}
[mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]];
}
return mutableRequest;
}
除此之外还有其他特殊的处理,比如对请求体的分界线处理等,这里不过多叙述
下面来看对请求结果的序列化
// 使用并行队列对响应进行处理,提升解析效率
dispatch_async(url_session_manager_processing_queue(), ^{
NSError *serializationError = nil;
responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError];
if (self.downloadFileURL) {responseObject = self.downloadFileURL;}
if (responseObject) {userInfo[AFNetworkingTaskDidCompleteSerializedResponseKey] = responseObject;
}
if (serializationError) {userInfo[AFNetworkingTaskDidCompleteErrorKey] = serializationError;
}
// 处理完成后还是 GCD 统一处理
dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{if (self.completionHandler) {self.completionHandler(task.response, responseObject, serializationError);
}
// 发出通知
dispatch_async(dispatch_get_main_queue(), ^{[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
});
});
});
接下来看 AFJSONResponseSerializer
- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error
{
// 第一步永远是校验数据合法性,这种编程习惯值得我们学习,实现功能的同时首先要考虑错误和非法输入
if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) {return nil;}
}
// 检查 body 是否为空格,对应 rails 的一种处理方式
BOOL isSpace = [data isEqualToData:[NSData dataWithBytes:" " length:1]];
if (data.length == 0 || isSpace) {return nil;}
NSError *serializationError = nil;
// 真正解析 json 的地方
id responseObject = [NSJSONSerialization JSONObjectWithData:data options:self.readingOptions error:&serializationError];
if (!responseObject)
{if (error) {*error = AFErrorWithUnderlyingError(serializationError, *error);
}
return nil;
}
// 移除 json 解析出来的 null 对象
if (self.removesKeysWithNullValues)
return AFJSONObjectByRemovingKeysWithNullValues(responseObject, self.readingOptions);
}
return responseObject;
}
json 解析就到这里了,主要能看出框架作者编程的模块化、安全编程等优秀的习惯
安全策略部分 AFSecurityPolicy
AFHTTPSessionManager 在初始化的时候使用了默认的安全策略 self.securityPolicy = [AFSecurityPolicy defaultPolicy]
,下面我们来研究下安全策略是干什么用的
我们都知道网络请求有非加密链接,在用浏览器打开一些杂七杂八的网站的时候,总会提示你当前的连接不安全,也即是说,如果你的网络请求没有使用 ssl 或 tls 加密算法加密,这一部分可以看这篇博客,当然,如果我们请求的服务使用了非自签名证书,并且该 CA 是默认被内置在苹果中的话我们是不需要额外配置什么东西的,但是事情总有例外,有些业务方出于一些需求需要使用自签名证书,这就需要我们配置 AFSecurityPolicy
+ (instancetype)defaultPolicy {AFSecurityPolicy *securityPolicy = [[self alloc] init];
securityPolicy.SSLPinningMode = AFSSLPinningModeNone;
return securityPolicy;
}
可以看到这里使用默认的配置 AFSSLPinningModeNone
typedef NS_ENUM(NSUInteger, AFSSLPinningMode) {
AFSSLPinningModeNone, // 只验证 iOS 系统内置的证书
AFSSLPinningModePublicKey, // 验证服务下发的证书(与本地证书),仅对比公钥
AFSSLPinningModeCertificate, // 验证服务下发的证书(与本地证书),会先验证域名和有效期等信息
};
对于获取服务器的公钥你可以使用以下命令
openssl s_client -showcerts -connect www.bing.com:443 </dev/null | openssl x509 -outform DER > server.cer
接下来将你所需要使用服务的公钥加入工程中,使用的时候可以如下配置
AFSecurityPolicy *policy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModePublicKey];
manager.securityPolicy = policy;
你可能会问,这里我们也没有指定证书啊,放心,我们看方法体
+ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode {
// 这个初始化方法里面传入了证书
return [self policyWithPinningMode:pinningMode withPinnedCertificates:[self defaultPinnedCertificates]];
}
// 会从当前的类被加载的 bundle 中获取
+ (NSSet *)defaultPinnedCertificates {
static NSSet *_defaultPinnedCertificates = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{NSBundle *bundle = [NSBundle bundleForClass:[self class]];
_defaultPinnedCertificates = [self certificatesInBundle:bundle];
});
return _defaultPinnedCertificates;
}
// 获取所有以.cer 结尾的文件
+ (NSSet *)certificatesInBundle:(NSBundle *)bundle {NSArray *paths = [bundle pathsForResourcesOfType:@"cer" inDirectory:@"."];
NSMutableSet *certificates = [NSMutableSet setWithCapacity:[paths count]];
for (NSString *path in paths) {NSData *certificateData = [NSData dataWithContentsOfFile:path];
[certificates addObject:certificateData];
}
return [NSSet setWithSet:certificates];
}
永远对代码有敬畏之心
很多人可能觉得写一个网络请求很简单,随随便便就能完成,可是不要忘了,在软件开发过程中开发只是第一步,而且不是权重最大的一部,如果你的代码模块复杂,逻辑混乱不堪那么在后续的维护中就会相当复杂,我上面总结了那么多的使用模块在外面的调用不过是一行 [[AFHTTPSessionManager manager] GET:@"http://httpbin.org/get" parameters:nil progress:nil success:nil failure:nil]
这么简单。在提升自己的路上,我们做的还有很多