iOS基础之网络请求

目录

       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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容