AFNetworking

74次阅读

共计 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 之前并行创建出来的 tasktaskIdentifier 不唯一的 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];
                });
            });
        });
    }
}

managertaskDelegate 部分先看到这里

序列化部分 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],下面我们来研究下安全策略是干什么用的

我们都知道网络请求有非加密链接,在用浏览器打开一些杂七杂八的网站的时候,总会提示你当前的连接不安全,也即是说,如果你的网络请求没有使用 ssltls 加密算法加密,这一部分可以看这篇博客,当然,如果我们请求的服务使用了非自签名证书,并且该 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] 这么简单。在提升自己的路上,我们做的还有很多

正文完
 0