内容字号:默认大号超大号

段落设置:段首缩进取消段首缩进

字体设置:切换到微软雅黑切换到宋体

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

2018-06-08 19:48 出处:清屏网 人气: 评论(0

目录

  • 前言
  • AFURLSessionManager
    • 业务流程
    • NSURLSession的代理方法
    • AFURLSessionManagerTaskDelegate
      • 任务进度
      • 承接方法
  • AFHTTPSessionManager
    • 业务流程
    • 分片上传
  • 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");  
    }];

API注释Demo

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

GitHub

参考


分享给小伙伴们:
本文标签: AFNetworkingiOS

相关文章

发表评论愿您的每句评论,都能给大家的生活添色彩,带来共鸣,带来思索,带来快乐。

CopyRight © 2015-2016 QingPingShan.com , All Rights Reserved.

清屏网 版权所有 豫ICP备15026204号