iOS源码补完计划--AFNetworking(一)

作者: kirito_song | 来源:发表于2018-05-15 17:53 被阅读156次

    目录

    • 前言
    • AFURLSessionManager
      • 业务流程
      • NSURLSession的代理方法
      • AFURLSessionManagerTaskDelegate
        • 任务进度
        • 承接方法
    • AFHTTPSessionManager
      • 业务流程
      • 分片上传
    • 一些比较有意思的东西
      • 在监听属性的时候、可以用NSStringFromSelector(@selector(xxx))这种方式来自动提示。
      • 功能AIP分层
      • 如何防止block循环引用
      • 把NSURLSession众多代理转化成了block
      • 消除编译器clang警告
      • 正则的简便写法
      • 如何做到对外只读、对内读写
    • API注释Demo
    • 参考

    前言

    AFNetworking源码第一篇
    主要看了看AFURLSessionManager以及AFHTTPSessionManager相关的API

    AFN概述:《iOS源码补完计划--AFNetworking 3.1.0源码研读》

    AFURLSessionManager

    业务流程

    先写一个最简单的网络请求

    NSURLSessionConfiguration * configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    AFURLSessionManager * manager = [[AFURLSessionManager alloc]initWithSessionConfiguration:configuration];
        
    //如果不添加反序列化、有可能会报错说传回来的res是text/html。
    manager.responseSerializer = [AFHTTPResponseSerializer serializer];
    
    NSURLRequest * request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.baidu.com"]];
    NSURLSessionDataTask * task = [manager dataTaskWithRequest:request completionHandler:^(NSURLResponse * _Nonnull response, id  _Nullable responseObject, NSError * _Nullable error) {
            
    }];
        
    [task resume];
    
    进入AFURLSessionManager内部:

    初始化一个新的AFURLSessionManager对象

    - (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
        self = [super init];
        if (!self) {
            return nil;
        }
    
        //容错
        if (!configuration) {
            configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
        }
    
        self.sessionConfiguration = configuration;
    
        self.operationQueue = [[NSOperationQueue alloc] init];
        //最大并发为1
        self.operationQueue.maxConcurrentOperationCount = 1;
        
        //初始化自己持有的NSURLSession对象
        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
    
        //mutableTaskDelegatesKeyedByTaskIdentifier 存放着 @{urltask:AFURLSessionManagerTaskDelegate}
        self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
    
        //对象锁 将来会操作 self.mutableTaskDelegatesKeyedByTaskIdentifier对象
        self.lock = [[NSLock alloc] init];
        self.lock.name = AFURLSessionManagerLockName;
    
        //清除磁盘和临时网络缓存。不过按理说刚初始化时应该是空的、可能不理解其中深意。
        [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
            NSLog(@"%@",dataTasks);
            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;
    }
    

    NSURLRequest生成一个NSURLSessionDataTask对象

    - (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;
        
        //内部针对不同iOS版本采用了不同的同步策略保证创建的NSURLSessionDataTask不唯一。具体可以到帖子上去看。
        url_session_manager_create_task_safely(^{
            dataTask = [self.session dataTaskWithRequest:request];
        });
    
        [self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
    
        return dataTask;
    }
    
    

    为每个NSURLSessionDataTask对象生成对应的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对象出来
        AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
        //与本类关联
        delegate.manager = self;
        //设置完成回调---由用户传入
        delegate.completionHandler = completionHandler;
        //添加个描述。具体为self对象的指针str
        dataTask.taskDescription = self.taskDescriptionForSessionTasks;
        //将代理和task关联
        //task.taskIdentifier 是由session统一分配的标识符
        [self setDelegate:delegate forTask:dataTask];
        //设置下载/上传的进度回调---由用户传入
        delegate.uploadProgressBlock = uploadProgressBlock;
        delegate.downloadProgressBlock = downloadProgressBlock;
    }
    

    关联task与其对应的delegate.将manager从一些无法绕开的数据处理以及传递中解放出来

    //关联task与其对应的delegate
    - (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
                forTask:(NSURLSessionTask *)task
    {
        //两个断言
        NSParameterAssert(task);
        NSParameterAssert(delegate);
    
        //线程安全
        [self.lock lock];
        //将delegate 与 task绑定
        self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
        //为AFTaskDelegate设置 task 的进度监听
        [delegate setupProgressForTask:task];
        //为任务添加监听、包括暂停和开始
        //后面还会hook暂停和开始的方法、触发监听
        [self addNotificationObserverForTask:task];
        
        [self.lock unlock];
    }
    
    NSURLSession的代理方法

    NSURLSession的代理被设置为AFURLSessionManager自身。

    self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
    

    所有的代理、都由AFURLSessionManager承接。


    这里manager实现了NSURLSession的所有代理方法。具体作用我感觉应该单开一篇文章来写、毕竟感觉对理解AFN有很大的用处。
    《iOS基础深入补完计划--网络模块NSURLSession概述》
    大致分为两种
    第一种:由manager自行处理、如果用户手动实现则交给用户处理
    //重定向
    - (void)URLSession:(NSURLSession *)session
                  task:(NSURLSessionTask *)task
    willPerformHTTPRedirection:(NSHTTPURLResponse *)response
            newRequest:(NSURLRequest *)request
     completionHandler:(void (^)(NSURLRequest *))completionHandler
    {
        //默认为当前链接
        NSURLRequest *redirectRequest = request;
    
        if (self.taskWillPerformHTTPRedirection) {
            //如果用户实现了重定向、则采用用户的反馈
            redirectRequest = self.taskWillPerformHTTPRedirection(session, task, response, request);
        }
    
        if (completionHandler) {
            completionHandler(redirectRequest);
        }
    }
    
    第二种:下放给AFURLSessionManagerTaskDelegate处理、如果用户手动实现了则(仅当移动文件时)作废。
    - (void)URLSession:(NSURLSession *)session
                  task:(NSURLSessionTask *)task
    didCompleteWithError:(NSError *)error
    {
        AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
    
        if (delegate) {
            //我之前一直很好奇为什么AFURLSessionManagerTaskDelegate与session没有绑定delegate也能调用代理方法
            //原来是由manager主动调用的--里面的回调是task级别、也就是创建task时的block
            [delegate URLSession:session task:task didCompleteWithError:error];
    
            [self removeDelegateForTask:task];
        }
        
        //你也可以手动回去完成信息--这个是session级别的。
        if (self.taskDidComplete) {
            self.taskDidComplete(session, task, error);
        }
    }
    
    //服务器成功返回数据
    - (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);
        }
    }
    
    //下载任务已经完成
    - (void)URLSession:(NSURLSession *)session
          downloadTask:(NSURLSessionDownloadTask *)downloadTask
    didFinishDownloadingToURL:(NSURL *)location
    {
        AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask];
        if (self.downloadTaskDidFinishDownloading) {
            //获取用户手动移动的位置
            NSURL *fileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location);
            if (fileURL) {
                delegate.downloadFileURL = fileURL;
                NSError *error = nil;
                [[NSFileManager defaultManager] moveItemAtURL:location toURL:fileURL error:&error];
                if (error) {
                    [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:error.userInfo];
                }
                //如果用户自己指定了位置、那就不自动移动文件了
                return;
            }
        }
        
        if (delegate) {
            [delegate URLSession:session downloadTask:downloadTask didFinishDownloadingToURL:location];
        }
    }
    

    AFURLSessionManagerTaskDelegate

    用来承接AFURLSessionManager截获的NSURLSession某些需要特别处理或者说绕不开(进度、任务结束、数据流拼接、下载完成后的文件移动)的代理。

    具体它要管理些什么、很凑巧、基本都展现在属性里了:

    @interface AFURLSessionManagerTaskDelegate : NSObject <NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate>
    @property (nonatomic, weak) AFURLSessionManager *manager;//弱持有manager 为了在必要的时候获取manager的队列、序列化、证书配置等信息
    @property (nonatomic, strong) NSMutableData *mutableData;//负责下载的数据组合
    @property (nonatomic, strong) NSProgress *uploadProgress;//上传进度
    @property (nonatomic, strong) NSProgress *downloadProgress;//下载进度
    @property (nonatomic, copy) NSURL *downloadFileURL;//文件储存位置、更多是记录作用
    @property (nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading;//文件储存位置(用户创建下载任务时必填)
    @property (nonatomic, copy) AFURLSessionTaskProgressBlock uploadProgressBlock;//上传任务进度传递
    @property (nonatomic, copy) AFURLSessionTaskProgressBlock downloadProgressBlock;//下载任务进度传递
    @property (nonatomic, copy) AFURLSessionTaskCompletionHandler completionHandler;//任务结束时间传递
    @end
    

    任务进度

    当监听属性发生改变。修改delegate对应progress。
    当自己的progress发生改变时、调用对应的block反馈给用户。

    #pragma mark - NSProgress Tracking
    //设置进度观察
    - (void)setupProgressForTask:(NSURLSessionTask *)task {
        __weak __typeof__(task) weakTask = task;
        
        //获取task上传/下载的预期大小
        self.uploadProgress.totalUnitCount = task.countOfBytesExpectedToSend;
        self.downloadProgress.totalUnitCount = task.countOfBytesExpectedToReceive;
        
        //可以取消
        [self.uploadProgress setCancellable:YES];
        
        [self.uploadProgress setCancellationHandler:^{
            __typeof__(weakTask) strongTask = weakTask;
            [strongTask cancel];
        }];
        [self.uploadProgress setPausable:YES];
        [self.uploadProgress setPausingHandler:^{
            __typeof__(weakTask) strongTask = weakTask;
            [strongTask suspend];
        }];
        if ([self.uploadProgress respondsToSelector:@selector(setResumingHandler:)]) {
            [self.uploadProgress setResumingHandler:^{
                __typeof__(weakTask) strongTask = weakTask;
                [strongTask resume];
            }];
        }
    
        [self.downloadProgress setCancellable:YES];
        [self.downloadProgress setCancellationHandler:^{
            __typeof__(weakTask) strongTask = weakTask;
            [strongTask cancel];
        }];
        [self.downloadProgress setPausable:YES];
        [self.downloadProgress setPausingHandler:^{
            __typeof__(weakTask) strongTask = weakTask;
            [strongTask suspend];
        }];
    
        if ([self.downloadProgress respondsToSelector:@selector(setResumingHandler:)]) {
            [self.downloadProgress setResumingHandler:^{
                __typeof__(weakTask) strongTask = weakTask;
                [strongTask resume];
            }];
        }
    
        
        //观察task属性
        [task addObserver:self
               forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))
                  options:NSKeyValueObservingOptionNew
                  context:NULL];
        [task addObserver:self
               forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))
                  options:NSKeyValueObservingOptionNew
                  context:NULL];
    
        [task addObserver:self
               forKeyPath:NSStringFromSelector(@selector(countOfBytesSent))
                  options:NSKeyValueObservingOptionNew
                  context:NULL];
        [task addObserver:self
               forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToSend))
                  options:NSKeyValueObservingOptionNew
                  context:NULL];
    
        //观察进度完成
        [self.downloadProgress addObserver:self
                                forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
                                   options:NSKeyValueObservingOptionNew
                                   context:NULL];
        [self.uploadProgress addObserver:self
                              forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
                                 options:NSKeyValueObservingOptionNew
                                 context:NULL];
    }
    
    - (void)cleanUpProgressForTask:(NSURLSessionTask *)task {
        [task removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))];
        [task removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))];
        [task removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesSent))];
        [task removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToSend))];
        [self.downloadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))];
        [self.uploadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))];
    }
    
    //当总大小、进度等有更新时。将与每个taskDelegate对象绑定的Progress传递出去
    //以达成用block控制不同任务Progress代理的需求
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
        if ([object isKindOfClass:[NSURLSessionTask class]] || [object isKindOfClass:[NSURLSessionDownloadTask class]]) {
            if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesReceived))]) {
                self.downloadProgress.completedUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];
            } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))]) {
                self.downloadProgress.totalUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];
            } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesSent))]) {
                self.uploadProgress.completedUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];
            } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToSend))]) {
                self.uploadProgress.totalUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];
            }
        }
        else if ([object isEqual:self.downloadProgress]) {
            if (self.downloadProgressBlock) {
                self.downloadProgressBlock(object);
            }
        }
        else if ([object isEqual:self.uploadProgress]) {
            if (self.uploadProgressBlock) {
                self.uploadProgressBlock(object);
            }
        }
    }
    

    承接方法

    任务结束:

    //任务结束
    - (void)URLSession:(__unused NSURLSession *)session
                  task:(NSURLSessionTask *)task
    didCompleteWithError:(NSError *)error
    {
    #pragma clang diagnostic push
    //忽略系统警告 大概是因为作者下面习惯使用A ?: B这种选择式
    #pragma clang diagnostic ignored "-Wgnu"
        __strong AFURLSessionManager *manager = self.manager;
    
        __block id responseObject = nil;
    
        __block NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
        //保存序列化器
        userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer;
    
        //Performance Improvement from #2672
        NSData *data = nil;
        if (self.mutableData) {
            data = [self.mutableData copy];
            //抛弃了self.mutableData的引用、释放出来一些内存。
            self.mutableData = nil;
        }
    
        if (self.downloadFileURL) {
            //保存下载文件储存的位置
            userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL;
        } else if (data) {
            //保存task获取到的原始数据
            userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = data;
        }
    
        if (error) {
            //请求出错
            
            //保存错误信息
            userInfo[AFNetworkingTaskDidCompleteErrorKey] = error;
            // 这里 A ?: B === A ? A : B;
            //如果用户没有定制、则使用AF提供的分组和队列
            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);
                }
                
                //通知
                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;
                }
                //如果用户没有定制、则使用AF提供的分组和队列
                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];
                    });
                });
            });
        }
    #pragma clang diagnostic pop
    }
    

    得到部分数据时:
    这里比我们手动实现NSURLSession要人性化得多。
    因为AFURLSessionManagerTaskDelegate是跟随任务的、数据容器mutableData自然也是跟随每个任务。不需要每次开启新任务的时候把原来的容器置空。

    //服务器返回了(可能是一部分)数据
    - (void)URLSession:(__unused NSURLSession *)session
              dataTask:(__unused NSURLSessionDataTask *)dataTask
        didReceiveData:(NSData *)data
    {
        //组合数据
        [self.mutableData appendData:data];
    }
    

    下载任务完成时:

    //下载任务完成
    - (void)URLSession:(NSURLSession *)session
          downloadTask:(NSURLSessionDownloadTask *)downloadTask
    didFinishDownloadingToURL:(NSURL *)location
    {
        NSError *fileManagerError = nil;
        self.downloadFileURL = nil;
    
        if (self.downloadTaskDidFinishDownloading) {
            //获取用户自定义存储位置
            self.downloadFileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location);
            if (self.downloadFileURL) {
                //将位于location原始位置的文件移动到自定义位置
                [[NSFileManager defaultManager] moveItemAtURL:location toURL:self.downloadFileURL error:&fileManagerError];
    
                if (fileManagerError) {
                    //如果失败了发个通知
                    [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:fileManagerError.userInfo];
                }
            }
        }
    }
    

    以上三个方法、由manager在同名的代理方法中主动调用。
    作用显而易见、两个完成的代理、一个记录receive data的代理。
    而manager在其中的作用

    • 如果用户实现了对应的block、单纯的将代理中的参数传递给用户。
    • 如果属于上述三个方法、交由AFURLSessionManagerTaskDelegate处理。

    AFHTTPSessionManager

    AFURLSessionManager针对HTTP请求特化的扩展。

    • 主要体现在Request上
      比如请求方式、请求头请求体的配置等等。
    • 仅向外提供了几个API
      GET、POST、HEAD、PUT以及DELETE
    • 以及三个属性
    /**
        请求链接
     */
    @property (readonly, nonatomic, strong, nullable) NSURL *baseURL;
    
    /**
        请求器、负责请求头、请求体等配置
     */
    @property (nonatomic, strong) AFHTTPRequestSerializer <AFURLRequestSerialization> * requestSerializer;
    
    /**
        解码器、负责响应解码。比如json格式等等
     */
    @property (nonatomic, strong) AFHTTPResponseSerializer <AFURLResponseSerialization> * responseSerializer;
    

    业务流程

    初始化

    - (instancetype)initWithBaseURL:(NSURL *)url
               sessionConfiguration:(NSURLSessionConfiguration *)configuration
    {
        self = [super initWithSessionConfiguration:configuration];
        if (!self) {
            return nil;
        }
    
        // 截取掉最后的'/'
        if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) {
            url = [url URLByAppendingPathComponent:@""];
        }
        //请求路径
        self.baseURL = url;
        //请求器
        self.requestSerializer = [AFHTTPRequestSerializer serializer];
        //解码器
        self.responseSerializer = [AFJSONResponseSerializer serializer];
    
        return self;
    }
    

    以GET请求举例

    - (NSURLSessionDataTask *)GET:(NSString *)URLString
                       parameters:(id)parameters
                         progress:(void (^)(NSProgress * _Nonnull))downloadProgress
                          success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
                          failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
    {
        //所有只需要url和参数的请求都要汇聚于此
        NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"
                                                            URLString:URLString
                                                           parameters:parameters
                                                       uploadProgress:nil
                                                     downloadProgress:downloadProgress
                                                              success:success
                                                              failure:failure];
    
        [dataTask resume];
    
        return dataTask;
    }
    
    - (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) {
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wgnu"
                dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
                    failure(nil, serializationError);
                });
    #pragma clang diagnostic pop
            }
    
            return nil;
        }
    
        __block NSURLSessionDataTask *dataTask = nil;
        //这个就回到AFURLSessionManager的原生方法了
        //通过req生成一个数据任务
        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正常的请求方法dataTaskWithRequest:之前、根据不同的请求方式生成了不同的Request、然后帮我们把任务resume启动而已。

    分片上传

    一个比较特殊的API

    - (NSURLSessionDataTask *)POST:(NSString *)URLString
                        parameters:(id)parameters
         constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block
                          progress:(nullable void (^)(NSProgress * _Nonnull))uploadProgress
                           success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
                           failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
    {
        NSError *serializationError = nil;
        //将block的数据以流的形式分片上传
        NSMutableURLRequest *request = [self.requestSerializer multipartFormRequestWithMethod:@"POST" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters constructingBodyWithBlock:block error:&serializationError];
        if (serializationError) {
            if (failure) {
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wgnu"
                dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
                    failure(nil, serializationError);
                });
    #pragma clang diagnostic pop
            }
    
            return nil;
        }
    
        //这个就回到AFURLSessionManager的原生方法了
        //通过req生成一个分段数据任务
        __block NSURLSessionDataTask *task = [self uploadTaskWithStreamedRequest:request progress:uploadProgress completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
            if (error) {
                if (failure) {
                    failure(task, error);
                }
            } else {
                if (success) {
                    success(task, responseObject);
                }
            }
        }];
    
        [task resume];
    
        return task;
    }
    

    里面的主线流程也是一样、唯一不用的是请求Request的生产方法.

    NSMutableURLRequest *request = [self.requestSerializer multipartFormRequestWithMethod:@"POST" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString]
    

    在用NSURLRequest上传文件时,一般是两种方法:

    • 一个是设置body、但是如果文件稍大的话,将会撑爆内存。

    • 另外一种则是创建一个临时文件、将数据拼接进去、然后将文件路径设置为bodyStream、这样就可以分片的上传了。

    • 而AFN则是更进一步的运用边传边拼的方式上传文件、这无疑是更加高端也是更加繁琐的方法。
      具体由于我现在还没有看到具体模块、有兴趣可以先看一下另一篇帖子《AFNetworking分析<二>》

    • 使用的话、到时可以举个例子

    [manager POST:@"http://localhost/demo/upload.php" parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {  
              
            NSURL *fileURL = [[NSBundle mainBundle] URLForResource:@"头像1.png" withExtension:nil];  
            
            NSString *fileName = @"fileName"  ;
             
            //AFN会持续的将formData中的数据取出并且转换成不同的AFHTTPBodyPart追加给AFMultipartBodyStream。
            //最后将这个AFMultipartBodyStream赋值给NSMutableURLRequest的bodyStream、在网络请求时交由NSURLConnection读取。
            [formData appendPartWithFileURL:fileURL name:@"uploadFile" fileName:fileName mimeType:@"image/png" error:NULL];  
              
        } success:^(AFHTTPRequestOperation *operation, id responseObject) {  
            NSLog(@"OK");  
        } failure:^(AFHTTPRequestOperation *operation, NSError *error) {  
            NSLog(@"error");  
        }];  
    

    一些比较有意思的东西

    • 在监听属性的时候、可以用NSStringFromSelector(@selector(xxx))这种方式来自动提示。

    因为属性本身就是与其get方法同名、可以降低出错概率。

    [self.uploadProgress addObserver:self
                              forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
                                 options:NSKeyValueObservingOptionNew
                                 context:NULL];
    
    • 功能AIP分层

    AFURLSessionManager实现了所有的NSURLSessionDelegate
    但同时又将其中某些需要处理复杂逻辑的代理传递给了AFURLSessionManagerTaskDelegate
    使得代码更清晰、逻辑更明确。
    需要注意的是、AFURLSessionManagerTaskDelegate完全包裹在了AFURLSessionManager内部、外界完全感受到他的存在。但是又能做数据处理、这个架构设计真心很赞。
    除此之外、AFURLSessionManagerAFHTTPSessionManager之间也做了很好的分层。
    你可以单独使用AFURLSessionManager进行网络会话、也可以通过AFHTTPSessionManager更好的使用AFURLSessionManager进行HTTP请求。

    • 如何防止block循环引用

    其实我几年前就听说AFN可以防止循环引用、但是一直没看。
    今天找了找发现似乎已经没有了这段代码
    所以个人推测现在不会引起循环引用的原因、应该是因为AFN都在作为单例使用、和self并不互相持有。

    贴一段以前别人帖子里的代码:
    //复写setCompletionBlock
    - (void)setCompletionBlock:(void (^)(void))block {
        [self.lock lock];
        if (!block) {
            [super setCompletionBlock:nil];
        } else {
            __weak __typeof(self)weakSelf = self;
            [super setCompletionBlock:^ {
                __strong __typeof(weakSelf)strongSelf = weakSelf;
    
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wgnu"
                //看有没有自定义的完成组,否则用AF的组
                dispatch_group_t group = strongSelf.completionGroup ?: url_request_operation_completion_group();
                //看有没有自定义的完成queue,否则用主队列
                dispatch_queue_t queue = strongSelf.completionQueue ?: dispatch_get_main_queue();
    #pragma clang diagnostic pop
                
                //调用设置的Block,在这个组和队列中
                dispatch_group_async(group, queue, ^{
                    block();
                });
    
                //结束时候置nil,防止循环引用
                dispatch_group_notify(group, url_request_operation_completion_queue(), ^{
                    [strongSelf setCompletionBlock:nil];
                });
            }];
        }
        [self.lock unlock];
    }
    
    
    • 把NSURLSession众多代理转化成了block

    这个说实话我并不太暂停...
    个人感觉block就是应该控制个数、而NSURLSession的代理加起来起码有二三十个。
    如果到了这种数量级的数据传递、真的还是用代理吧、饶了我。

    • 消除编译器clang警告

    其中Wgnu可以换成其他具体命令

    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wgnu"
    #pragma clang diagnostic pop
    
    • 正则的简便写法

    讲道理我还真第一次见

    `A ?: B = A ? A : B`
    
    • 如何做到对外只读、对内读写
    .h中
    @property (readonly, nonatomic, strong, nullable) NSURL *baseURL;
    .m中
    @property (readwrite, nonatomic, strong) NSURL *baseURL;
    

    API注释Demo

    把注释的源码放在了github上、有兴趣可以自取。

    GitHub


    参考

    【原】AFNetworking源码阅读(四)
    AFNetworking分析<二>
    iOS基础深入补完计划--网络模块NSURLSession概述

    相关文章

      网友评论

        本文标题:iOS源码补完计划--AFNetworking(一)

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