美文网首页
AFN源码阅读笔记(一)看看一个请求怎么生成

AFN源码阅读笔记(一)看看一个请求怎么生成

作者: yiangdea | 来源:发表于2017-06-27 23:08 被阅读104次

    AFNetworking 3.0源码阅读笔记

    框架重要类介绍

    • NSURLSession

    • AFURLSessionManager AFHTTPSessionManager

    • 序列化 AFURLRequestSerialization AFURLResponseSerialization

    • 附加功能 AFSecurityPolicy AFNetworkReachabilityManager

    AFN架构体示意图

    其中 AFURLSessionManager 是 AFHTTPSessionManager 的父类

    AFURLSessionManager 负责生成 NSURLSession 的实例,管理 AFSecurityPolicy 和 AFNetworkReachabilityManager,来保证请求的安全和查看网络连接情况,它有一个 AFJSONResponseSerializer 的实例来序列化 HTTP 响应

    AFHTTPSessionManager 有着自己的 AFHTTPRequestSerializer 和 AFJSONResponseSerializer 来管理请求和响应的序列化,同时依赖父类提供的接口保证安全、监控网络状态,实现发出 HTTP 请求这一核心功能

    AFURLSessionManager 请求和管理核心

    • 负责创建和管理 NSURLSession

    • 管理 NSURLSessionTask

    • 实现 NSURLSessionDelegate 等协议中的代理方法

    • 使用 AFURLSessionManagerTaskDelegate 管理进度

    • 引入 AFSecurityPolicy 保证请求的安全

    • 引入 AFNetworkReachabilityManager 监控网络状态

    • 使用 AFURLSessionTaskSwizzling 调剂方法

    创建和管理 NSURLSession

    
    由 AFURLSessionManager 的初始化方法:
    
    - (instancetype)initWithSessionConfiguration:
    
    (nullable NSURLSessionConfiguration *)configuration;
    
    进行展开:
    
    

    设计模式-- 构造函数,参数控制在三个以内。

    正常业务逻辑会出现参数过多的情况。

    解决方案
    
    - (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)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;
    
    }
    
    

    该方法主要完成如下工作:

    初始化会话配置(NSURLSessionConfiguration),默认为 defaultSessionConfiguration

    设置相应的 OperationQueue,决定请求过程中的一系列事件在哪个 OperationQueue 回调,这里是设置了最大并发量为 1 的队列,也就相当于串行队列了。

    初始化会话(session),并设置会话的代理及代理队列,delegate 用来处理请求中的各种事件,可以设置为 nil 使用系统提供的 delegate,

    另外,NSURLSession 对象是强引用了 delegate

    如果程序最终没有调用 invalidateAndCancel 方法来 invalidate 该 session 的话,则会造成内存泄漏

    初始化管理响应序列化(AFJSONResponseSerializer),安全认证(AFSecurityPolicy)以及监控网络状态(AFNetworkReachabilityManager)的实例

    初始化保存 data task 的字典(mutableTaskDelegatesKeyedByTaskIdentifier)

    创建和管理 NSURLSessionDataTask

    
    - (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;
    
    }
    
    

    设计模式: NSError真滴好用。特别是对于串行的错误判断代码。

    
    // 创建request对象
    
    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;
    
    }
    
    
    
    

    // mark:YD ARC下,autoreleasrpool依然好用。

    // 猜测可能是由于如果用strong,则会导致NSError对象。在整个方法周期内。不会被释放掉。为了节省内存。使用__autoreleasing,在其作用域以外释放

    next: 后来证明这个猜测只有部分猜对了.
    事实上,**类型作为参数传入方法里.即便不加autoreleasrpool修饰符,系统也会帮助添加;
    真正的原因是跟函数作用域相关.传入的参数会有指针拷贝操作,系统为了其生成的指针在作用域负责释放,故添加修饰.
    PS : **传入地址.拷贝的也就是指向指针的指针了.用完是要释放掉的,但地址并不能一定在其作用域内释放.详情见部分系统方法传入&error这种类型.地址在方法外也会被引用.
    故只能用__autoreleasing修饰,在其地址真正无作用时再释放掉.
    参考资料: http://daiyi.pro/2017/01/07/%E4%BA%8C%E7%BA%A7%E6%8C%87%E9%92%88%E4%B8%8EARC%E4%B8%8D%E4%B8%BA%E4%BA%BA%E7%9F%A5%E7%9A%84%E7%89%B9%E6%80%A7/

    
    - (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;
    
    // mark:YD 利用kvo,将manager上有关于request的值,赋值给当前的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;
    
    }
    
    

    实现 NSURLSessionDelegate 等协议中的代理方法

    首先,NSURLSession 的代理对象结构如下:
    • NSURLSessionDelegate

    NSURLSessionTaskDelegate,遵守 NSURLSessionDelegate 协议

    NSURLSessionDataDelegate,遵守 NSURLSessionTaskDelegate 协议,是网络请求通常遵循的协议,常用的方法:

    接受到服务响应时调用的方法:

    
    - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler;
    
    /**
    
    * 必须在该方法中对服务器的响应进行授权,才能继续接收服务器返回的数据,调用如下函数
    
    * completionHandler(NSURLSessionResponseAllow)
    
    */
    
    

    接收到服务器返回的数据时调用的方法

    
    - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
    
    /**
    
    * data:服务返回的数据,通常为 JSON 格式数据
    
    */
    
    

    请求完成时调用的方法(成功或失败)

    
    - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(nullable NSError *)error
    
    /**
    
    * 若出现错误,error 中存放错误信息
    
    */
    
    
    • NSURLSessionDownloadDelegate(通常用于下载大量数据),遵守 NSURLSessionTaskDelegate 协议,常用的方法:

    写入数据到临时文件时调用的方法(服务器返回一点就写入一点)

    
    - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite: (int64_t)totalBytesExpectedToWrite
    
    /**
    
    * totalBytesWritten,已写入数据的总长度
    
    * totalBytesExpectedToWrite:总共要写入数据的总长度
    
    * 可以在该方法中计算下载进度
    
    */
    
    

    遇到错误的时候调用

    
    - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
    
    /**
    
    *error:若遇到错误,则保存错误信息
    
    */
    
    

    用于断点下载的方法

    
    - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes
    
    /**
    
    * fileOffset:继续下载时,文件的开始位置
    
    * expectedTotalBytes:剩余的数据总数
    
    */
    
    

    下载完成时调用的方法

    
    - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
    
    /**
    
    * location:下载的文件保存的临时位置
    
    * 需要将下载的文件保存在可以长期保存的位置
    
    */
    
    

    使用 AFURLSessionManagerTaskDelegate 管理进度

    是在太TMD复杂,留着以后再看

    使用 _AFURLSessionTaskSwizzling 调剂方法

    AFURLSessionTaskSwizzling 的唯一功能就是修改 NSURLSessionTask 的 resume 和 suspend 方法,使用下面的方法替换原有的实现:

    
    - (void)af_resume {
    
    NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state");
    
    NSURLSessionTaskState state = [self state];
    
    [self af_resume];
    
    if (state != NSURLSessionTaskStateRunning) {
    
    [[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidResumeNotification object:self];
    
    }
    
    }
    
    - (void)af_suspend {
    
    NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state");
    
    NSURLSessionTaskState state = [self state];
    
    [self af_suspend];
    
    if (state != NSURLSessionTaskStateSuspended) {
    
    [[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidSuspendNotification object:self];
    
    }
    
    }
    
    
    • 这样做的目的是为了在方法 resume 或者 suspend 被调用时发出通知。具体方法调剂的过程是在 + load 方法中进行的
    
    + (void)load {
    
    if (NSClassFromString(@"NSURLSessionTask")) {
    
    NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
    
    // 首先构建一个 NSURLSession 对象 session,再通过 session 构建出一个 _NSCFLocalDataTask 变量
    
    NSURLSession * session = [NSURLSession sessionWithConfiguration:configuration];
    
    #pragma GCC diagnostic push
    
    #pragma GCC diagnostic ignored "-Wnonnull"
    
    NSURLSessionDataTask *localDataTask = [session dataTaskWithURL:nil];
    
    #pragma clang diagnostic pop
    
    // 获取到 af_resume 实现的指针
    
    IMP originalAFResumeIMP = method_getImplementation(class_getInstanceMethod([self class], @selector(af_resume)));
    
    Class currentClass = [localDataTask class];
    
    // 检查当前 class 是否实现了 resume。如果实现了,继续第 4 步
    
    while (class_getInstanceMethod(currentClass, @selector(resume))) {
    
    // 获取到当前 class 的父类(superClass)
    
    Class superClass = [currentClass superclass];
    
    // 获取到当前 class 对于 resume 实现的指针
    
    IMP classResumeIMP = method_getImplementation(class_getInstanceMethod(currentClass, @selector(resume)));
    
    // 获取到父类对于 resume 实现的指针
    
    IMP superclassResumeIMP = method_getImplementation(class_getInstanceMethod(superClass, @selector(resume)));
    
    // 如果当前 class 对于 resume 的实现和父类不一样(类似 iOS 7 上的情况),并且当前 class 的 resume 实现和 af_resume 不一样,才进行 method swizzling
    
    if (classResumeIMP != superclassResumeIMP &&
    
    originalAFResumeIMP != classResumeIMP) {
    
    [self swizzleResumeAndSuspendMethodForClass:currentClass];
    
    }
    
    // 设置当前操作的 class 为其父类 class,重复步骤 3~8
    
    currentClass = [currentClass superclass];
    
    }
    
    

    引入 AFSecurityPolicy 保证请求的安全

    AFSecurityPolicy 是 AFNetworking 用来保证 HTTP 请求安全的类,它被 AFURLSessionManager 持有,如果你在 AFURLSessionManager 的实现文件中搜索 self.securityPolicy,你只会得到三条结果:

    
    初始化 self.securityPolicy = [AFSecurityPolicy defaultPolicy]
    
    

    收到连接层的验证请求时

    任务接收到验证请求时

    在 API 调用上,后两者都调用了

    
    - [AFSecurityPolicy evaluateServerTrust:forDomain:]
    
    

    方法来判断当前服务器是否被信任。

    引入 AFNetworkReachabilityManager 监控网络状态

    没看,下次再说。。

    相关文章

      网友评论

          本文标题:AFN源码阅读笔记(一)看看一个请求怎么生成

          本文链接:https://www.haomeiwen.com/subject/oxjycxtx.html