目录
1. AFN框架
2. NSURLSession
3. NSURLConnection
4. 其他
Cocoa 中网络编程层次结构分为三层,自上而下分别是:
Cocoa 层:NSURL,Bonjour,Game Kit,WebKit
Core Foundation 层:基于 C 的 CFNetwork 和 CFNetServices
OS 层:基于 C 的 BSD socket
1. AFN框架(第三方库)常用
// 1.创建网络请求manager
AFHTTPSessionManager *manger=[AFHTTPSessionManager manager];
// 1.1 设置请求的数据类型
// 设置 request类型为二进制类型(默认)
[manger setRequestSerializer:[AFHTTPRequestSerializer serializer]];
// 设置 请求超时时间
[manger.requestSerializer setTimeoutInterval:6.f];
// 1.2 设置 response
// 设置 response类型为二进制类型(默认:JSON类型,已经解析)
[manger setResponseSerializer:[AFHTTPResponseSerializer serializer]];
// 设置 允许接收的数据类型
[manger.responseSerializer setAcceptableContentTypes:[NSSet setWithObjects:@"application/json",@"text/json", @"text/javascript",@"text/html", nil]];
// 2.发送请求
// url
NSString *urlStr=@"url";
// 参数(可以是数组/字典/nil)
NSDictionary *paraDic=@{@"userId":@""};
[manger POST:urlStr parameters:paraDic progress:^(NSProgress * _Nonnull uploadProgress) {
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
// 主线程:可以直接更新UI
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"%@发生错误: \n%@",urlStr,error);
}];
'
请求格式
AFHTTPRequestSerializer 二进制格式 (默认)
AFJSONRequestSerializer JSON
AFPropertyListRequestSerializer PList(是一种特殊的XML,解析起来相对容易)
返回格式
AFHTTPResponseSerializer 二进制格式 (不作任何处理:NSData,当返回的数据不是JSON/XML/plist/image要设置,如:HTML、Text)
AFJSONResponseSerializer JSON (默认)
AFXMLParserResponseSerializer XML,只能返回XMLParser,还需要自己通过代理方法解析
AFXMLDocumentResponseSerializer (Mac OS X)
AFPropertyListResponseSerializer PList
AFImageResponseSerializer Image
AFCompoundResponseSerializer 组合
'
上传
// url
NSString *urlStr=@"url";
// 参数(可以是数组/字典/nil)
NSDictionary *paraDic=@{@"userId":@""};
[manger POST:urlStr parameters:paraDic constructingBodyWithBlock:^(id<AFMultipartFormData> _Nonnull formData) {
// img->data
NSData *imgData=UIImagePNGRepresentation([UIImage imageNamed:@""]);
// 设置需要上传的文件(需要上传的文件,后台规定的参数名,文件名,后台规定的文件类型)
[formData appendPartWithFileData:imgData name:@"headImage" fileName:@"hello.png" mimeType:@"image/png"];
} progress:^(NSProgress * _Nonnull uploadProgress) {
//
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
// 上传成功
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"%@发生错误: \n%@",urlStr,error);
}];
2. NSURLSession (原生网络请求类---目前用)
// 1.创建请求
NSURLRequest *request=[NSURLRequest requestWithURL:[NSURL URLWithString:@"urlStr"]];
// 2.创建会话
NSURLSession *session=[NSURLSession sharedSession];
// 3.创建任务
NSURLSessionDataTask *task=[session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if(error==nil){
NSDictionary *dcit=[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil];
// 刷新UI在主线程中
}
}];
// 3.1启动任务
[task resume];
继承关系:
NSObject
NSURLSessionTask
NSURLSessionDataTask NSURLSessionDownloadTask
NSURLSessionUploadTask
说明:
NSURLSessionUploadTask 上传专用Task(不接收数据)
NSURLSessionDownloadTask 下载专用Task
NSURLSessionDataTask 上传数据,并接收返回数据
0.创建NSURLRequest (3方式)
方式一
NSURLRequest *request=[NSURLRequest requestWithURL:[NSURL URLWithString:@""]];
方式二
NSURLRequest *request=[NSURLRequest requestWithURL:[NSURL URLWithString:@""] cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:2];
方式三
NSMutableURLRequest *muRequest=[NSMutableURLRequest requestWithURL:[NSURL URLWithString:@""]];
// 设置请求超时时间
[muRequest setTimeoutInterval:10];
// 默认GET,设置请求方式
[muRequest setHTTPMethod:@"POST"];
// 设置请求体
[muRequest setHTTPBody:[@"key=value&key2=value2" dataUsingEncoding:NSUTF8StringEncoding]];
// 设置请求头
[muRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
// [muRequest addValue:@"" forHTTPHeaderField:@"Content-Length"];
1. 创建NSURLSession(3方式)
方式一 全局Session(有局限)
NSURLSession *session=[NSURLSession sharedSession];
方式二 SessionConfiguration
NSURLSessionConfiguration *connfi=[NSURLSessionConfiguration defaultSessionConfiguration];
[connfi setTimeoutIntervalForRequest:5]; // 设置请求超时
NSURLSession *session=[NSURLSession sessionWithConfiguration:connfi];
NSURLSession *session=[NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[NSOperationQueue mainQueue]];
后台Session
NSURLSessionConfiguration *config=[NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@""];
NSURLSession *session=[NSURLSession sessionWithConfiguration:connfi];
2. 创建NSURLSessionConfiguration (3方式)
方式一
// 存储Cache在硬盘(默认模式,保存用户的证书到钥匙串,使用共享cookie存储)
NSURLSessionConfiguration *config=[NSURLSessionConfiguration defaultSessionConfiguration];
方式二
// 存储Cache在内存(用于无痕浏览,会话结束后清空数据)
NSURLSessionConfiguration *config=[NSURLSessionConfiguration ephemeralSessionConfiguration];
方式三
// 将上传下载移到后台
NSURLSessionConfiguration *config=[NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@""];
3. 创建NSURLSessionDataTask (4方式)
方式一
NSURLSessionDataTask *dataTask=[session dataTaskWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@""]]];
方式二
NSURLSessionDataTask *dataTask=[session dataTaskWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@""]] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if(!error){
}
}];
方式三
NSURLSessionDataTask *dataTask=[session dataTaskWithURL:[NSURL URLWithString:@""]];
方式四
NSURLSessionDataTask *dataTask=[session dataTaskWithURL:[NSURL URLWithString:@""] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if(!error){
}
}];
[dataTask resume]; // 任务开始
[dataTask suspend]; // 任务暂停
[dataTask cancel]; // 任务取消
4. 创建NSURLSessionUploadTask(5方式) 上传data
方式一
[session uploadTaskWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@""]] fromData:data];
方式二
[session uploadTaskWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@""]] fromData:data completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if(!error){
}
}];
方式三
[session uploadTaskWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@""]] fromFile:[[NSURL alloc]initFileURLWithPath:[NSString stringWithFormat:@"%@/Documents/1.txt",NSHomeDirectory()]]];
方式四
[session uploadTaskWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@""]] fromFile:[[NSURL alloc]initFileURLWithPath:[NSString stringWithFormat:@"%@/Documents/1.txt",NSHomeDirectory()]] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if(!error){
}
}];
方式五
[session uploadTaskWithStreamedRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@""]]];
2. 创建NSURLSessionDownloadTask(6方式)下载data
方式一
NSURLSessionDownloadTask *dataTask=[session downloadTaskWithURL:[NSURL URLWithString:@""]];
方式二
NSURLSessionDownloadTask *dataTask=[session downloadTaskWithURL:[NSURL URLWithString:@""] completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if(!error){
}
}];
方式三
NSURLSessionDownloadTask *dataTask=[session downloadTaskWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@""]]];
方式四
NSURLSessionDownloadTask *dataTask=[session downloadTaskWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@""]] completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if(!error){
}
}];
方式五
NSURLSessionDownloadTask *dataTask=[session downloadTaskWithResumeData:data];
方式六
NSURLSessionDownloadTask *dataTask=[session downloadTaskWithResumeData:data completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if(!error){
}
}];
3. NSURLConnection(原生网络请求类---已过时)
方式一:使用dele(异步)
<NSURLConnectionDelegate,NSURLConnectionDataDelegate>
@property (nonatomic,strong) NSMutableData *contentData;
// 1.创建请求,建立连接
NSURLRequest *request=[NSURLRequest requestWithURL:[NSURL URLWithString:@"urlStr"]];
NSURLConnection *conn=[NSURLConnection connectionWithRequest:request delegate:self];
[conn start];
#pragma mark dele
// 2.收到响应时调用
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
_contentData.length=0;
}
// 3.收到数据时调用
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
[_contentData appendData:data];
}
// 4.数据接收完毕后调用
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{}
// 4.1连接出错时调用
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{}
方式二:sendAsynchronousRequest(异步)
[NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"urlStr"]] queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
}];
方式三:sendSynchronousRequest(同步)
NSData *contentData=[NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"urlStr"]] returningResponse:&response error:nil];
上面提到的 NSURLConnection 的异步方法实际上还是跑在主线程当中,在主线程中执行网络操作会带来两个问题:
尽管在网络连接过程中不会对主线程造成阻塞,但是 delegate 的回调方法还是在主线程中执行的。如果我们在回调方法中(特别是 completion 回调)中进行了大量的耗时操作,仍然会造成主线程的阻塞。
NSURLConnection 默认会跑在当前的 runloop 中,并且跑在 Default Mode,当用户执行滚动的 UI 操作时会发生 runloop mode 的切换,也就导致了 NSURLConnection 不能及时执行和完成回调。
简单地把start函数放到后台的 queue 中是不行的。因为 dispatch_async 开出的线程中,默认 runloop 没有执行,因此线程会立即结束,来不及调用回调方法。
dispatch_async(connectionQueue, ^{
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:[NSURL URLWithString:[NSString stringWithFormat:someURL]]];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self]; // 没有设置 startImmediately 为 NO,会立即开始
//[connection start]; 这一句没有必要写,写了也一样不能 work。
});
这样又带来一个问题,这个线程中 runloop 会一直跑着,导致这个线程也一直不结束
dispatch_async(connectionQueue, ^{
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:[NSURL URLWithString:[NSString stringWithFormat:someURL]]];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[[NSRunLoop currentRunLoop] run];
});
dispatch_async(connectionQueue, ^{
NSRunLoop* runLoop = [NSRunLoop currentRunLoop];
[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode]; // 添加 inputSource,让 runloop 保持 alive
[self.connection scheduleInRunLoop:runLoop
forMode:NSDefaultRunLoopMode];
[self.connection start];
[runLoop run];
});
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{
CFRunLoopStop(CFRunLoopGetCurrent());
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
CFRunLoopStop(CFRunLoopGetCurrent());
}
方法二(使用NSOperationQueue)
dispatch_async(connectionQueue, ^{
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:aURLRequest
delegate:self
startImmediately:NO];
[connection setDelegateQueue:[[NSOperationQueue alloc] init]];
[connection start];
});
4. 其他
注意:
1.App如果需要进行网络操作,则要在info.plist文件中添加权限:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
菊花
// 是否打开菊花(状态栏上:用来提示用户正在请求网络)
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:true];
data<->str
// data->str
NSString *str=[[NSString alloc]initWithData:[NSData new] encoding:NSUTF8StringEncoding];
// str->data
NSData *data=[@"" dataUsingEncoding:NSUTF8StringEncoding];
中文
对中文编码(url中有中文)
string.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLFragmentAllowedCharacterSet())!
对中文解码
paraStr.stringByRemovingPercentEncoding
Charles(抓包)
1. 下载charles
2. 连上同一Wifi,手机设置wifi为手动:服务器(mac终端:ifconfig en0 找到地址) 端口:8888(默认:charles的Proxy中设置的)
3. OK
XML(过时, 现只用于存储,不用于传输)
用来存储和传递数据(优点:可读性强,缺点:太冗余)
文档由节点(开始标签和结束标签组成)构成
DOM解析:
一次性将整个XML文档加载进内存,比较适合解析小文件,例如:GDataXml解析
SAX解析:
从根元素开始,按顺序一个元素一个元素往下解析,比较适合解析大文件,例如:NSXMLParser解析
例:
<?xml version="1.0" encoding="UTF-8">
<books> 根节点
<book id="0"> id是属性节点
<name>book1</name> name是元素节点
<price>12</price>
</book>
</books>
使用(2方式):
—————————方式一
第一步:
1. 导入Data文件,建立桥接文件 : #import "GDataXMLNode.h"
2. Build Phase | Link Binary With Libraries 添加libxml2.tbd
3. Build Phase | Compile Source .m文件后双击 + -fno-objc-arc
4. BuildSettings | header Search Path + /usr/include/libxml2
第二步:
let path=NSBundle.mainBundle().pathForResource("xml", ofType: "txt")
let data=NSData.init(contentsOfFile: path!)
let doc=try! GDataXMLDocument.init(data: data, options: 0) // 解析数据
let rootE=doc.rootElement() // 获取根节点
print(rootE.XMLString()) // 打印节点
// 获取节点的内容(根据节点名)
let booksArr=rootE.elementsForName("books") as! [GDataXMLElement] // 获取元素节点(返回数组)
let booksEl=booksArr[0]
let bookArr=booksEl.elementsForName("book") as! [GDataXMLElement]
for bookEl in bookArr{
let name=(bookEl.elementsForName("name")[0]) as! GDataXMLElement //
let attName=bookEl.attributes()[0] as! GDataXMLNode // 获取属性节点(返回数组)
print(name.stringValue()) // 节点值 name.name()节点名
}
—————————方式二
XPath 使用路径表达式获取节点
/ 从根节点获取
// 查询和名称相同的节点(不考虑位置)
. 获取当前节点
.. 获取当前节点的父节点
@ 获取属性
// 位置
let pathT="/root/books/book/name"
// 查询所有 符合pathT位置 的节点
let nameArr=try! doc.nodesForXPath(pathT)
print(nameArr[0].stringValue())
扩展:
根名 获取 根名 元素的所有子节点
/根名 获取 根名 元素的所有子节点
/根名/元素名 获取 和路径匹配的所有元素节点
//元素名 获取 元素名相同的所有元素节点
根名/元素名//元素名 获取 元素名相同的所有元素节点(在根名/元素名 下)
//@属性名 获取 属性名相同 的所有元素节点
/根名/元素名[1] 获取第一个满足path的元素节点
[last()] 最后一个
[position()<3] 前2个
[属性名>3]/元素名
//title[@length] 或取所有 属性名length的元素名title 的元素
//title[@length='e'] 或取所有 属性名length且值为e的元素名title 的元素
* 匹配任何元素节点。
@* 匹配任何属性节点。
node() 匹配任何类型的节点。
| 多个路径
监测网络状态 (需引入AFN框架)
1. cocoaPods
pod 'AFNetworking'
2.AppDelegate+
#import <AFNetworking/AFNetworking.h>
// 监听网络状态
-(void)mangeNet{
// 1.获取网络管理者
AFNetworkReachabilityManager *netManger=[AFNetworkReachabilityManager sharedManager];
// 2.网络状态发生变化后调用
[netManger setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
switch (status) {
case AFNetworkReachabilityStatusUnknown:{
// 检测到网络状态前为此状态
NSLog(@"网络未知");
}
break;
case AFNetworkReachabilityStatusNotReachable:{
NSLog(@"连接不到网络");
// 提示用户,跳到系统设置页设置网络
}
break;
case AFNetworkReachabilityStatusReachableViaWWAN:{
NSLog(@"流量");
}
break;
case AFNetworkReachabilityStatusReachableViaWiFi:{
NSLog(@"wifi");
}
break;
}
}];
// 3.监测网络变化
[netManger startMonitoring];
}
// 获取当前网络状态
AFNetworkReachabilityStatus status=netManger.networkReachabilityStatus;
// 跳转到设置---蜂窝网络
[[UIApplication sharedApplication]openURL:[NSURL URLWithString:@"App-Prefs:root=MOBILE_DATA_SETTINGS_ID"]];
@interface AFHTTPSessionManager : AFURLSessionManager <NSSecureCoding, NSCopying>
// 获取相对路径(readonly)
/**
相对路径的使用
NSURL *baseURL = [NSURL URLWithString:@"http://example.com/v1/"];
[NSURL URLWithString:@"foo" relativeToURL:baseURL]; // http://example.com/v1/foo
[NSURL URLWithString:@"foo?bar=baz" relativeToURL:baseURL]; // http://example.com/v1/foo?bar=baz
[NSURL URLWithString:@"/foo" relativeToURL:baseURL]; // http://example.com/foo
[NSURL URLWithString:@"foo/" relativeToURL:baseURL]; // http://example.com/v1/foo
[NSURL URLWithString:@"/foo/" relativeToURL:baseURL]; // http://example.com/foo/
[NSURL URLWithString:@"http://example2.com/" relativeToURL:baseURL]; // http://example2.com/
*/
@property (readonly, nonatomic, strong, nullable) NSURL *baseURL;
/**
以什么格式序列化请求体,默认:AFHTTPRequestSerializer
requestSerializer不能为nil
*/
@property (nonatomic, strong) AFHTTPRequestSerializer <AFURLRequestSerialization> * requestSerializer;
/**
以什么格式序列化响应体,默认:AFJSONResponseSerializer
responseSerializer不能为nil
*/
@property (nonatomic, strong) AFHTTPResponseSerializer <AFURLResponseSerialization> * responseSerializer;
///-------------------------------
/// @name Managing Security Policy
///-------------------------------
/**
The security policy used by created session to evaluate server trust for secure connections. `AFURLSessionManager` uses the `defaultPolicy` unless otherwise specified. A security policy configured with `AFSSLPinningModePublicKey` or `AFSSLPinningModeCertificate` can only be applied on a session manager initialized with a secure base URL (i.e. https). Applying a security policy with pinning enabled on an insecure session manager throws an `Invalid Security Policy` exception.
*/
@property (nonatomic, strong) AFSecurityPolicy *securityPolicy;
///---------------------
/// @name Initialization
///---------------------
// 获取AFHTTPSessionManager对象
+ (instancetype)manager;
/**
Initializes an `AFHTTPSessionManager` object with the specified base URL.
@param url The base URL for the HTTP client.
@return The newly-initialized HTTP client
*/
- (instancetype)initWithBaseURL:(nullable NSURL *)url;
/**
Initializes an `AFHTTPSessionManager` object with the specified base URL.
This is the designated initializer.
@param url The base URL for the HTTP client.
@param configuration The configuration used to create the managed session.
@return The newly-initialized HTTP client
*/
- (instancetype)initWithBaseURL:(nullable NSURL *)url
sessionConfiguration:(nullable NSURLSessionConfiguration *)configuration NS_DESIGNATED_INITIALIZER;
///---------------------------
/// @name Making HTTP Requests
///---------------------------
/**
创建并运行一个NSURLSessionDataTask的GET方式任务请求
内部调用dataTaskWithHTTPMethod方法
URLString:请求字符串
parameters:参数字典(根据requestSerializer序列化)
headers:追加至默认的请求头
downloadProgress:下载过程中的回调,不在主线程队列中执行。
success:请求成功并获得响应时调用
failure:出错时的回调(请求出错、解析出错)
*/
- (nullable NSURLSessionDataTask *)GET:(NSString *)URLString
parameters:(nullable id)parameters
headers:(nullable NSDictionary <NSString *, NSString *> *)headers
progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgress
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
/**
Creates and runs an `NSURLSessionDataTask` with a `HEAD` request.
@param URLString The URL string used to create the request URL.
@param parameters The parameters to be encoded according to the client request serializer.
@param headers The headers appended to the default headers for this request.
@param success A block object to be executed when the task finishes successfully. This block has no return value and takes a single arguments: the data task.
@param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
@see -dataTaskWithRequest:completionHandler:
*/
- (nullable NSURLSessionDataTask *)HEAD:(NSString *)URLString
parameters:(nullable id)parameters
headers:(nullable NSDictionary <NSString *, NSString *> *)headers
success:(nullable void (^)(NSURLSessionDataTask *task))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
/**
创建并运行一个NSURLSessionDataTask的POST方式任务请求
内部调用dataTaskWithHTTPMethod方法
URLString:请求字符串
parameters:参数字典(根据requestSerializer序列化)
headers:追加至默认的请求头
uploadProgress:上传过程中的回调,不在主线程队列中执行。
success:请求成功并获得响应时调用
failure:出错时的回调(请求出错、解析出错)
*/
- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
parameters:(nullable id)parameters
headers:(nullable NSDictionary <NSString *, NSString *> *)headers
progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
/**
创建并运行一个NSURLSessionDataTask的POST方式任务请求(用于上传图片等)
内部调用uploadTaskWithStreamedRequest方法
URLString:请求字符串
parameters:参数字典(根据requestSerializer序列化)
headers:追加至默认的请求头
block:追加data至请求体
uploadProgress:上传过程中的回调,不在主线程队列中执行。
success:请求成功并获得响应时调用
failure:出错时的回调(请求出错、解析出错)
*/
- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
parameters:(nullable id)parameters
headers:(nullable NSDictionary <NSString *, NSString *> *)headers
constructingBodyWithBlock:(nullable void (^)(id <AFMultipartFormData> formData))block
progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
/**
Creates and runs an `NSURLSessionDataTask` with a `PUT` request.
@param URLString The URL string used to create the request URL.
@param parameters The parameters to be encoded according to the client request serializer.
@param headers The headers appended to the default headers for this request.
@param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
@param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
@see -dataTaskWithRequest:completionHandler:
*/
- (nullable NSURLSessionDataTask *)PUT:(NSString *)URLString
parameters:(nullable id)parameters
headers:(nullable NSDictionary <NSString *, NSString *> *)headers
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
/**
Creates and runs an `NSURLSessionDataTask` with a `PATCH` request.
@param URLString The URL string used to create the request URL.
@param parameters The parameters to be encoded according to the client request serializer.
@param headers The headers appended to the default headers for this request.
@param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
@param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
@see -dataTaskWithRequest:completionHandler:
*/
- (nullable NSURLSessionDataTask *)PATCH:(NSString *)URLString
parameters:(nullable id)parameters
headers:(nullable NSDictionary <NSString *, NSString *> *)headers
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
/**
Creates and runs an `NSURLSessionDataTask` with a `DELETE` request.
@param URLString The URL string used to create the request URL.
@param parameters The parameters to be encoded according to the client request serializer.
@param headers The headers appended to the default headers for this request.
@param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
@param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
@see -dataTaskWithRequest:completionHandler:
*/
- (nullable NSURLSessionDataTask *)DELETE:(NSString *)URLString
parameters:(nullable id)parameters
headers:(nullable NSDictionary <NSString *, NSString *> *)headers
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
/**
创建并运行一个NSURLSessionDataTask任务请求
内部调用dataTaskWithHTTPMethod方法
method:POST、GET
URLString:请求字符串
parameters:参数字典(根据requestSerializer序列化)
headers:追加至默认的请求头
uploadProgress:上传过程中的回调,不在主线程队列中执行。
downloadProgress:下载过程中的回调,不在主线程队列中执行。
success:请求成功并获得响应时调用
failure:出错时的回调(请求出错、解析出错)
*/
- (nullable NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(nullable id)parameters
headers:(nullable NSDictionary <NSString *, NSString *> *)headers
uploadProgress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress
downloadProgress:(nullable void (^)(NSProgress *downloadProgress))downloadProgress
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
@end
//
- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
parameters:(nullable id)parameters
headers:(nullable NSDictionary <NSString *, NSString *> *)headers
progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure
{
// 创建NSURLSessionDataTask并执行
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"POST" URLString:URLString parameters:parameters headers:headers uploadProgress:uploadProgress downloadProgress:nil success:success failure:failure];
[dataTask resume];
return dataTask;
}
//
- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(nullable id)parameters
headers:(nullable NSDictionary <NSString *, NSString *> *)headers
uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress
downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure
{
// 创建NSMutableURLRequest请求
NSError *serializationError = nil;
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
// 设置head请求头
for (NSString *headerField in headers.keyEnumerator) {
[request setValue:headers[headerField] forHTTPHeaderField:headerField];
}
// 出错
if (serializationError) {
if (failure) {
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(nil, serializationError);
});
}
return nil;
}
// 创建NSURLSessionDataTask任务,并返回
__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回调
failure(dataTask, error);
}
} else {
if (success) { // succes回调
success(dataTask, responseObject);
}
}
}];
return dataTask;
}
//
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler {
// 创建NSURLSessionDataTask任务
NSURLSessionDataTask *dataTask = [self.session dataTaskWithRequest:request];
[self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
return dataTask;
}
//
- (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask
uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:dataTask];
delegate.manager = self;
delegate.completionHandler = completionHandler;
delegate.uploadProgressBlock = uploadProgressBlock;
delegate.downloadProgressBlock = downloadProgressBlock;
dataTask.taskDescription = self.taskDescriptionForSessionTasks;
[self setDelegate:delegate forTask:dataTask];
}
//
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
forTask:(NSURLSessionTask *)task
{
NSParameterAssert(task);
NSParameterAssert(delegate);
[self.lock lock];
self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
[self addNotificationObserverForTask:task];
[self.lock unlock];
}
- (void)addNotificationObserverForTask:(NSURLSessionTask *)task {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidResume:) name:AFNSURLSessionTaskDidResumeNotification object:task];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidSuspend:) name:AFNSURLSessionTaskDidSuspendNotification object:task];
}
- (void)taskDidResume:(NSNotification *)notification {
NSURLSessionTask *task = notification.object;
if ([task respondsToSelector:@selector(taskDescription)]) {
if ([task.taskDescription isEqualToString:self.taskDescriptionForSessionTasks]) {
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidResumeNotification object:task];
});
}
}
}
- (void)taskDidSuspend:(NSNotification *)notification {
NSURLSessionTask *task = notification.object;
if ([task respondsToSelector:@selector(taskDescription)]) {
if ([task.taskDescription isEqualToString:self.taskDescriptionForSessionTasks]) {
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidSuspendNotification object:task];
});
}
}
}
AFURLSessionManagerTaskDelegate
@interface AFURLSessionManagerTaskDelegate : NSObject <NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate>
- (instancetype)initWithTask:(NSURLSessionTask *)task;
@property (nonatomic, weak) AFURLSessionManager *manager;
@property (nonatomic, strong) NSMutableData *mutableData;
@property (nonatomic, strong) NSProgress *uploadProgress;
@property (nonatomic, strong) NSProgress *downloadProgress;
@property (nonatomic, copy) NSURL *downloadFileURL;
#if AF_CAN_INCLUDE_SESSION_TASK_METRICS
@property (nonatomic, strong) NSURLSessionTaskMetrics *sessionTaskMetrics AF_API_AVAILABLE(ios(10), macosx(10.12), watchos(3), tvos(10));
#endif
@property (nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading;
@property (nonatomic, copy) AFURLSessionTaskProgressBlock uploadProgressBlock;
@property (nonatomic, copy) AFURLSessionTaskProgressBlock downloadProgressBlock;
@property (nonatomic, copy) AFURLSessionTaskCompletionHandler completionHandler;
@end
@implementation AFURLSessionManagerTaskDelegate
- (instancetype)initWithTask:(NSURLSessionTask *)task {
self = [super init];
if (!self) {
return nil;
}
_mutableData = [NSMutableData data];
_uploadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil];
_downloadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil];
__weak __typeof__(task) weakTask = task;
for (NSProgress *progress in @[ _uploadProgress, _downloadProgress ])
{
progress.totalUnitCount = NSURLSessionTransferSizeUnknown;
progress.cancellable = YES;
progress.cancellationHandler = ^{
[weakTask cancel];
};
progress.pausable = YES;
progress.pausingHandler = ^{
[weakTask suspend];
};
#if AF_CAN_USE_AT_AVAILABLE
if (@available(macOS 10.11, *))
#else
if ([progress respondsToSelector:@selector(setResumingHandler:)])
#endif
{
progress.resumingHandler = ^{
[weakTask resume];
};
}
[progress addObserver:self
forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
options:NSKeyValueObservingOptionNew
context:NULL];
}
return self;
}
- (void)dealloc {
[self.downloadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))];
[self.uploadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))];
}
#pragma mark - NSProgress Tracking
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
if ([object isEqual:self.downloadProgress]) {
if (self.downloadProgressBlock) {
self.downloadProgressBlock(object);
}
}
else if ([object isEqual:self.uploadProgress]) {
if (self.uploadProgressBlock) {
self.uploadProgressBlock(object);
}
}
}
static const void * const AuthenticationChallengeErrorKey = &AuthenticationChallengeErrorKey;
#pragma mark - NSURLSessionTaskDelegate
- (void)URLSession:(__unused NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
error = objc_getAssociatedObject(task, AuthenticationChallengeErrorKey) ?: error;
__strong AFURLSessionManager *manager = self.manager;
__block id responseObject = nil;
NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer;
//Performance Improvement from #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;
}
#if AF_CAN_USE_AT_AVAILABLE && AF_CAN_INCLUDE_SESSION_TASK_METRICS
if (@available(iOS 10, macOS 10.12, watchOS 3, tvOS 10, *)) {
if (self.sessionTaskMetrics) {
userInfo[AFNetworkingTaskDidCompleteSessionTaskMetrics] = self.sessionTaskMetrics;
}
}
#endif
if (self.downloadFileURL) {
userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL;
} else if (data) {
userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = data;
}
if (error) {
userInfo[AFNetworkingTaskDidCompleteErrorKey] = error;
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;
}
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];
});
});
});
}
}
#if AF_CAN_INCLUDE_SESSION_TASK_METRICS
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics AF_API_AVAILABLE(ios(10), macosx(10.12), watchos(3), tvos(10)) {
self.sessionTaskMetrics = metrics;
}
#endif
#pragma mark - NSURLSessionDataDelegate
- (void)URLSession:(__unused NSURLSession *)session
dataTask:(__unused NSURLSessionDataTask *)dataTask
didReceiveData:(NSData *)data
{
self.downloadProgress.totalUnitCount = dataTask.countOfBytesExpectedToReceive;
self.downloadProgress.completedUnitCount = dataTask.countOfBytesReceived;
[self.mutableData appendData:data];
}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
didSendBodyData:(int64_t)bytesSent
totalBytesSent:(int64_t)totalBytesSent
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend{
self.uploadProgress.totalUnitCount = task.countOfBytesExpectedToSend;
self.uploadProgress.completedUnitCount = task.countOfBytesSent;
}
#pragma mark - NSURLSessionDownloadDelegate
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didWriteData:(int64_t)bytesWritten
totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{
self.downloadProgress.totalUnitCount = totalBytesExpectedToWrite;
self.downloadProgress.completedUnitCount = totalBytesWritten;
}
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didResumeAtOffset:(int64_t)fileOffset
expectedTotalBytes:(int64_t)expectedTotalBytes{
self.downloadProgress.totalUnitCount = expectedTotalBytes;
self.downloadProgress.completedUnitCount = fileOffset;
}
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
self.downloadFileURL = nil;
if (self.downloadTaskDidFinishDownloading) {
self.downloadFileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location);
if (self.downloadFileURL) {
NSError *fileManagerError = nil;
if (![[NSFileManager defaultManager] moveItemAtURL:location toURL:self.downloadFileURL error:&fileManagerError]) {
[[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:fileManagerError.userInfo];
} else {
[[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidMoveFileSuccessfullyNotification object:downloadTask userInfo:nil];
}
}
}
}
@end