AFNetworking详解
2020-12-24 本文已影响0人
Jey
AFNetworking3.0主要是对NSURLSession的封装,NSURLConnection是iOS9以前的
NSURLSession,网络通信核心类
- AFURLSessionManager
- AFHTTPSessionManager
AFHTTPSessionManager继承AFURLSessionManager
序列化
-
< AFURLRequestSerialization> 协议
-- AFHTTPRequestSerializer 根类
-- AFJSONRequestSerializer
-- AFPropertyListRequestSerializer -
< AFURLResponseSerialization> 协议
-- AFHTTPResponseSerializer 根类
-- AFJSONResponseSerializer
-- AFXMLParserResponseSerializerXMLParser
-- AFXMLDocumentResponseSerializer (Mac OS X) 不适合我们的iOS
-- AFPropertyListResponseSerializer plist
-- AFImageResponseSerializer //国片
-- AFCompoundResponseSerializer //混合
1. 初始化session
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
/*
1.调用父类的方法
2.给url添加“/”
3.给requestSerializer、responseSerializer设置默认值
*/
- (instancetype)initWithBaseURL:(NSURL *)url
sessionConfiguration:(NSURLSessionConfiguration *)configuration
{
//调用父类初始化方法
self = [super initWithSessionConfiguration:configuration];
if (!self) {
return nil;
}
// Ensure terminal slash for baseURL path, so that NSURL +URLWithString:relativeToURL: works as expected
/*
为了确保NSURL +URLWithString:relativeToURL: works可以正确执行,在baseurlpath的最后添加‘/’
*/
//url有值且没有‘/’,那么在url的末尾添加‘/’
if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) {
url = [url URLByAppendingPathComponent:@""];
}
self.baseURL = url;
//给requestSerializer、responseSerializer设置默认值
self.requestSerializer = [AFHTTPRequestSerializer serializer];
self.responseSerializer = [AFJSONResponseSerializer serializer];
return self;
}
2. 设置了一些默认值,返回数据的格式之类
/*
1.初始化一个session
2.给manager的属性设置初始值
*/
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
self = [super init];
if (!self) {
return nil;
}
//设置默认的configuration,配置我们的session
if (!configuration) {
configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
}
//持有configuration
self.sessionConfiguration = configuration;
// 设置为delegate的操作队列并发的线程数量1,也就是串行队列,返回数据
self.operationQueue = [[NSOperationQueue alloc] init];
self.operationQueue.maxConcurrentOperationCount = 1;
/*
-如果完成后需要做复杂(耗时)的处理,可以选择异步队列
-如果完成后直接更新UI,可以选择主队列
[NSOperationQueue mainQueue]
*/
self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
//默认为json解析
self.responseSerializer = [AFJSONResponseSerializer serializer];
//设置默认证书 无条件信任证书https认证
self.securityPolicy = [AFSecurityPolicy defaultPolicy];
#if !TARGET_OS_WATCH
//网络状态监听
self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
#endif
//delegate= value taskid = key
self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
//使用NSLock确保线程安全
self.lock = [[NSLock alloc] init];
self.lock.name = AFURLSessionManagerLockName;
//异步的获取当前session的所有未完成的task。其实讲道理来说在初始化中调用这个方法应该里面一个task都不会有
//后台任务重新回来初始化session,可能就会有先前的任务
//https://github.com/AFNetworking/AFNetworking/issues/3499
[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;
}
3. 创建一个NSMutableURLRequest
//1.生成request,2.通过request成成task
- (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;
/*
1.先调用AFHTTPRequestSerializer的requestWithMethod函数构建request
2.处理request构建产生的错误 – serializationError
//relativeToURL表示将URLString拼接到baseURL后面
*/
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
if (serializationError) {
if (failure) {
//http://fuckingclangwarnings.com/#semantic
//xcode忽略编译器的警告,diagnostic:诊断的
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
//completionQueue不存在返回dispatch_get_main_queue
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(nil, serializationError);
});
#pragma clang diagnostic pop
}
return nil;
}
//此时的request已经将参数拼接在url后面
__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;
}
给NSMutableURLRequest自带的属性赋值,将传入的参数进行编码处理等
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(id)parameters
error:(NSError *__autoreleasing *)error
{
//断言如果nil,直接打印出来
NSParameterAssert(method);
NSParameterAssert(URLString);
//我们传进来的是一个字符串,在这里它帮你转成url
NSURL *url = [NSURL URLWithString:URLString];
NSParameterAssert(url);
NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
//设置请求方式(get、post、put。。。)
mutableRequest.HTTPMethod = method;
//将request的各种属性遍历,给NSMutableURLRequest自带的属性赋值
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
//给设置过得的属性,添加到request(如:timeout)
if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
//通过kvc动态的给mutableRequest添加value
[mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
}
}
//将传入的参数进行编码,拼接到url后并返回 coount=5&start=1
mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];
NSLog(@"request'''''''''%@",mutableRequest);
return mutableRequest;
}
#pragma mark - AFURLRequestSerialization
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
withParameters:(id)parameters
error:(NSError *__autoreleasing *)error
{
NSParameterAssert(request);
NSMutableURLRequest *mutableRequest = [request mutableCopy];
//给mutableRequest.headfiled赋值
/*
1.请求行(状态行):get,url,http协议1.1
2.请求头:conttent-type,accept-language
3.请求体:get/post get参数拼接在url后面 post数据放在body
*/
[self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
if (![request valueForHTTPHeaderField:field]) {
[mutableRequest setValue:value forHTTPHeaderField:field];
}
}];
//将我们传入的字典转成字符串
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 {
//默认解析方式,dic- count=5&start=1
switch (self.queryStringSerializationStyle) {
case AFHTTPRequestQueryStringDefaultStyle:
//将parameters传入这个c函数
query = AFQueryStringFromParameters(parameters);
break;
}
}
}
//count=5&start=1
NSLog(@"query:%@",query);
//最后判断该request中是否包含了GET、HEAD、DELETE(都包含在HTTPMethodsEncodingParametersInURI)。因为这几个method的query是拼接到url后面的。而POST、PUT是把query拼接到http body中的。
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 {
// #2864: an empty string is a valid x-www-form-urlencoded payload
if (!query) {
query = @"";
}
//函数会判断request的Content-Type是否设置了,如果没有,就默认设置为application/x-www-form-urlencoded
//application/x-www-form-urlencoded是常用的表单发包方式,普通的表单提交,或者js发包,默认都是通过这种方式
if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
[mutableRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
}
//设置请求体
[mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]];
}
return mutableRequest;
}
4. 通过request生成task
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);
}
}
}];
5. 给task添加代理
taskId作为key,delegate作为value,存储在一个字典中self.mutableTaskDelegatesKeyedByTaskIdentifier
[self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
/*
注意addDelegateForDataTask:这个函数并不是AFURLSessionManagerTaskDelegate的函数,而是AFURLSessionManager的一个函数。这也侧面说明了AFURLSessionManagerTaskDelegate和NSURLSessionTask的关系是由AFURLSessionManager管理的。
*/
- (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
{
// 初始化delegate
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
delegate.manager = self;
delegate.completionHandler = completionHandler;
/*
taskidentifier=key delegate=value,确保task唯一
taskDescription自行设置的,区分是否是当前的session创建的
*/
dataTask.taskDescription = self.taskDescriptionForSessionTasks;
//函数字面意思是将一个session task和一个AFURLSessionManagerTaskDelegate类型的delegate变量绑在一起,而这个绑在一起的工作是由我们的AFURLSessionManager所做。至于绑定的过程,就是以该session task的taskIdentifier为key,delegate为value,赋值给mutableTaskDelegatesKeyedByTaskIdentifier这个NSMutableDictionary类型的变量。知道了这两者是关联在一起的话,马上就会产生另外的问题 —— 为什么要关联以及怎么关联在一起?
[self setDelegate:delegate forTask:dataTask];
//设置回调块
delegate.uploadProgressBlock = uploadProgressBlock;
delegate.downloadProgressBlock = downloadProgressBlock;
}
为task设置关联的delegate,,放入self.mutableTaskDelegatesKeyedByTaskIdentifier
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
forTask:(NSURLSessionTask *)task
{
//task和delegate都不能为空
NSParameterAssert(task);
NSParameterAssert(delegate);
//加锁确保中间代码块是原子操作,线程安全
[self.lock lock];
//将delegate存入字典,以taskid作为key,说明每个task都有各自的代理
self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
//设置两个NSProgress的变量 - uploadProgress和downloadProgress,给session task添加了两个KVO事件
[delegate setupProgressForTask:task];
[self addNotificationObserverForTask:task];
[self.lock unlock];
}
监听task的事件
#pragma mark - NSProgress Tracking
- (void)setupProgressForTask:(NSURLSessionTask *)task {
__weak __typeof__(task) weakTask = task;
//拿到上传下载期望的数据大小
self.uploadProgress.totalUnitCount = task.countOfBytesExpectedToSend;
self.downloadProgress.totalUnitCount = task.countOfBytesExpectedToReceive;
//设置这两个NSProgress对应的cancel、pause和resume这三个状态,正好对应session task的cancel、suspend和resume三个状态
[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和progress添加kvo,监测进度大小
[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];
//fractionCompleted:任务已经完成的比例,取值为0~1
[self.downloadProgress addObserver:self
forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
options:NSKeyValueObservingOptionNew
context:NULL];
[self.uploadProgress addObserver:self
forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
options:NSKeyValueObservingOptionNew
context:NULL];
}
```
#####6. session的系统回调,拿到绑定的delegate做数据处理
```
// 这里拼接data
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
didReceiveData:(NSData *)data
{
// 调用的是AFURLSessionManagerTaskDelegate的didReceiveData方法
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask];
[delegate URLSession:session dataTask:dataTask didReceiveData:data];
if (self.dataTaskDidReceiveData) {
self.dataTaskDidReceiveData(session, dataTask, data);
}
}
```
最终都会到这里
```
/*
task完成之后的回调,成功和失败都会回调这里
函数讨论:
注意这里的error不会报告服务期端的error,他表示的是客户端这边的error,比如无法解析hostname或者连不上host主机。
*/
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
// delegate may be nil when completing a task in the background
// 如果task是在后台完成的,可能delegate会为nil
if (delegate) {
[delegate URLSession:session task:task didCompleteWithError:error];
// 该task结束了,就移除对应的delegate
[self removeDelegateForTask:task];
}
//自定义Block回调
if (self.taskDidComplete) {
self.taskDidComplete(session, task, error);
}
}
```
在AFURLSessionManagerTaskDelegate中的方法中,数据正常返回的话,开启并行队列解析数据375行,block返回
```
//AF实现的代理!被从urlsession那转发到这
/*
第一个是获取数据,将responseSerializer和downloadFileURL或data存到userInfo里面
第二个是根据error是否为空值,做下一步处理
*/
- (void)URLSession:(__unused NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
//1)强引用self.manager,防止被提前释放;因为self.manager声明为weak,类似Block
__strong AFURLSessionManager *manager = self.manager;
__block id responseObject = nil;
// 因为NSNotification这个类中本身有userInfo属性,可作为响应函数的参数
// 不过我在AFNetworking源码中还未发现使用userInfo作为参数的做法,可能需要用户自己实现
/**
* userInfo中的key值例举如下:
* AFNetworkingTaskDidCompleteResponseDataKey session 存储task获取到的原始response数据,与序列化后的response有所不同
* AFNetworkingTaskDidCompleteSerializedResponseKey 存储经过序列化(serialized)后的response
* AFNetworkingTaskDidCompleteResponseSerializerKey 保存序列化response的序列化器(serializer)
* AFNetworkingTaskDidCompleteAssetPathKey 存储下载任务后,数据文件存放在磁盘上的位置
* AFNetworkingTaskDidCompleteErrorKey 错误信息
*/
//用来存储一些相关信息,来发送通知用的
__block NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
//存储responseSerializer响应解析对象
userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer;
//Performance Improvement from #2672
//具体可以查看#issue 2672。这里主要是针对大文件的时候,性能提升会很明显
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;
}
//继续给userinfo填数据
////如果downloadFileURL存在,即是下载任务就设置下载完成后的文件存储url到字典中
if (self.downloadFileURL) {
userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL;
} else if (data) {
//否则就设置对应的NSData数据到字典中
userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = data;
}
// 如果task出错了,处理error信息
// 所以对应的观察者在处理error的时候,比如可以先判断userInfo[AFNetworkingTaskDidCompleteErrorKey]是否有值,有值的话,就说明是要处理error
if (error) {
userInfo[AFNetworkingTaskDidCompleteErrorKey] = error;
// 这里用group方式来运行task完成方法,表示当前所有的task任务完成,才会通知执行其他操作
// 如果没有实现自定义的completionGroup和completionQueue,那么就使用AFNetworking提供的私有的dispatch_group_t和提供的dispatch_get_main_queue内容
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 {//在没有error时,会先对数据进行一次序列化操作,然后下面的处理就和有error的那部分一样了
dispatch_async(url_session_manager_processing_queue(), ^{
NSError *serializationError = nil;
// 根据对应的task和data将response data解析成可用的数据格式,比如JSON serializer就将data解析成JSON格式
responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError];
// 注意如果有downloadFileURL,意味着data存放在了磁盘上了,所以此处responseObject保存的是data存放位置,供后面completionHandler处理。没有downloadFileURL,就直接使用内存中的解析后的data数据
if (self.downloadFileURL) {
responseObject = self.downloadFileURL;
}
//写入userInfo
if (responseObject) {
userInfo[AFNetworkingTaskDidCompleteSerializedResponseKey] = responseObject;
}
// 序列化的时候出现错误
if (serializationError) {
userInfo[AFNetworkingTaskDidCompleteErrorKey] = serializationError;
}
//回调结果
//同理,在dispatch组中和特定队列执行回调块
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
}
```
注意这里的是通过主队列发送出去
```
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];
});
});
```
#### 序列化
requestSerialization:请求前的配置
reponseSerialization:请求后
##### post上传文件-----------------------------------------------
```
// 1. 使用AFHTTPSessionManager的接口
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
NSDictionary *dic = @{@"businessType":@"CC_USER_CENTER",
@"fileType":@"image",
@"file":@"img.jpeg"
};
[manager POST:@"http://xxx/api/demo/test/file" parameters:dic constructingBodyWithBlock:^(id<AFMultipartFormData> _Nonnull formData) {
// 在这个block中设置需要上传的文件
NSString *path = [[NSBundle mainBundle] pathForResource:@"1" ofType:@"png"];
// 将本地图片数据拼接到formData中 指定name
[formData appendPartWithFileURL:[NSURL fileURLWithPath:path] name:@"file" error:nil];
// 或者使用这个接口拼接 指定name和filename
// NSData *picdata =[NSData dataWithContentsOfFile:path];
// [formData appendPartWithFileData:picdata name:@"image" fileName:@"image.jpg" mimeType:@"image/jpeg"];
} progress:^(NSProgress * _Nonnull uploadProgress) {
NSLog(@"progress --- %@",uploadProgress.localizedDescription);
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(@"responseObject-------%@", responseObject);
dispatch_async(dispatch_get_main_queue(), ^{
});
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"Error-------%@", error);
}];
```
```
//构建一个multipartForm的request。并且通过`AFMultipartFormData`类型的formData来构建请求体
- (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(NSDictionary *)parameters
constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block
error:(NSError *__autoreleasing *)error
{
NSParameterAssert(method);
//method不能是get、head
NSParameterAssert(![method isEqualToString:@"GET"] && ![method isEqualToString:@"HEAD"]);
NSMutableURLRequest *mutableRequest = [self requestWithMethod:method URLString:URLString parameters:nil error:error];
// 使用initWithURLRequest:stringEncoding:来初始化一个AFStreamingMultipartFormData变量
// 主要是为了构建bodyStream
__block AFStreamingMultipartFormData *formData = [[AFStreamingMultipartFormData alloc] initWithURLRequest:mutableRequest stringEncoding:NSUTF8StringEncoding];
if (parameters) {
for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {
NSData *data = nil;
if ([pair.value isKindOfClass:[NSData class]]) {
data = pair.value;
} else if ([pair.value isEqual:[NSNull null]]) {
data = [NSData data];
} else {
//通常nslog打印会调用description,打印出来的是地址,但是可以重写description,来实现打印出我们想要的类型
data = [[pair.value description] dataUsingEncoding:self.stringEncoding];
}
if (data) {
// bodyStream构造最主要的部分就在这了(虽然后面requestByFinalizingMultipartFormData函数还会稍微处理一下)
// 根据data和name构建Request的header和body,后面详解
[formData appendPartWithFormData:data name:[pair.field description]];
}
}
}
// 参考上面的例子,其实还是往formData中添加数据
if (block) {
block(formData);
}
// 做最终的处理,比如设置一下MultipartRequest的bodyStream或者其特有的content-type等等,后面也会详解
return [formData requestByFinalizingMultipartFormData];
}
```
1.使用AFStreamingMultipartFormData初始化formdata,里面是AFMultipartBodyStream的实例
2. 把参数转成data,拼接[formData appendPartWithFormData:data name:[pair.field description]];转成AFHTTPBodyPart,添加到一个数组中
3. block回去继续添加图片数据,中间有分隔符要注意下
注意:post文件,content-type是application/x-www-form-urlencoded,formdata是UTF-8 data流数据, AFStreamingMultipartFormData实现分片上传,避免内存暴增
普通post,content-type是applecation/json,formdata是json