• iOS 编程:NSURLSession


    定义:描述 Foundation 框架类在标准的网络传输协议下,用 URLs 连接因特网并与服务器交互的一整套体系。

    支持的传输协议:

    • File Transfer Protocol (ftp://)
    • Hypertext Transfer Protocol (http://)
    • Hypertext Transfer Protocol with encryption (https://)
    • Local file URLs (file:///)
    • Data URLs (data://)

    结构图

    网络系统模块

    5个模块:代理支持、身份验证和凭据、cookie 存储、配置管理和缓存管理。

    Cookie,有时也用其复数形式 Cookies,指某些网站为了辨别用户身份、进行 session 跟踪而储存在用户本地终端上的数据(通常经过加密)。

    NSURLSessionTask

    NSURLSessionDelegate 委托协议

    Session 会话的概念

    Session中任务的行为取决于三个方面:

    1. Session 的类型(取决于创建的配置对象类型);
    2. task 任务的类型;
    3. task 任务被创建时,app 是否处于前台状态?

    Session 的类型

    • 默认会话(Default session):与其他用于下载URL的 Foundation 方法类似。 使用永久性的基于磁盘的缓存并将凭据存储在用户的钥匙串中。
    • 短暂会话(Ephemeral session):不会将任何数据存储到磁盘; 所有缓存,凭证等都保存在 RAM 中并与会话相关联。 因此,当应用程序使会话无效时,会自动清除该会话。
    • 后台会话(Background session):类似于默认会话,但是会使用单独的进程处理所有数据传输。 后台会话有一些额外的限制。

    ⚠️ NSURLSession 使用完需要释放,否则会引起内存泄漏问题。

    task 任务类型

    NSURLSession 支持三种类型的任务:data tasks, download tasks 和 upload tasks。

    • data tasks:使用 NSData 对象发送和接收数据。 处理应用程序与服务器之间的简短的,经常交互的请求。 数据任务可以在每次接收到数据后就返回,或者通过 completion handler 一次性返回所有数据到您的应用程序。
    • download tasks:下载任务以文件的形式检索数据,并在应用程序未运行时支持后台下载。
    • upload tasks:上传任务以文件的形式发送数据,并在应用程序未运行时支持后台上传。

    后台传输注意事项

    NSURLSession 类在您的应用程序被暂停时支持后台传输。 后台传输仅由使用后台会话配置对象(调用 backgroundSessionConfiguration :返回的会话)提供。

    • 必须提供委托对象来进行事件传递。 (对于上传和下载任务,代理的行为与在进程内传输相同。)
    • 只支持HTTP和HTTPS协议(没有自定义协议)。
    • 始终遵循重定向。
    • 只支持 file 文件上传(应用程序退出后,data 或 stream 类型的传输将会失败)。
    • 如果在应用程序处于后台时启动后台传输,配置对象的 discretionary 属性将被视为true。

    网络请求创建流程

    1
    2
    3
    4
    5
    graph LR
    NSURL-->NSURLRequest
    NSURLRequest-->NSURLSessionTask
    NSURLSessionConfiguration-->NSURLSession
    NSURLSession-->NSURLSessionTask

    网络请求流程图.png

    创建和配置 Session

    一、NSURLSessionDataTask 示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    // 1.创建 NSURLSessionConfiguration
    NSURLSessionConfiguration *defaultConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
    NSURLSessionConfiguration *ephemeralConfiguration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
    NSURLSessionConfiguration *backgroundConfiguration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier: @"com.myapp.networking.background"];

    // 配置默认会话的缓存行为
    NSString *cachesDirectory = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject;
    NSString *cachePath = [cachesDirectory stringByAppendingPathComponent:@"MyCache"];

    /* Note:
    iOS需要设置相对路径:〜/Library/Caches
    OS X 要设置绝对路径。
    */
    NSURLCache *cache = [[NSURLCache alloc] initWithMemoryCapacity:16384
    diskCapacity:268435456
    diskPath:cachePath];
    defaultConfiguration.URLCache = cache;
    defaultConfiguration.requestCachePolicy = NSURLRequestUseProtocolCachePolicy;

    // 2.创建 NSURLSession
    NSOperationQueue *operationQueue = [NSOperationQueue mainQueue];

    NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration:defaultConfiguration delegate:self delegateQueue:operationQueue];
    NSURLSession *ephemeralSession = [NSURLSession sessionWithConfiguration:ephemeralConfiguration delegate:self delegateQueue:operationQueue];
    NSURLSession *backgroundSession = [NSURLSession sessionWithConfiguration:backgroundConfiguration delegate:self delegateQueue:operationQueue];

    // 3.创建 NSURLSessionDataTask
    NSURL *url = [NSURL URLWithString:@"https://www.example.com/"];

    [[defaultSession dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
    NSLog(@"Got response %@ with error %@.n", response, error);
    NSLog(@"DATA:n%@nEND DATAn", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
    }] resume];

    二、NSURLSessionDownloadTask 示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    NSURL *url = [NSURL URLWithString:@"https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/ObjC_classic/FoundationObjC.pdf"];

    // 1.创建 NSURLSessionConfiguration
    NSURLSessionConfiguration *backgroundConfiguration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier: @"com.myapp.networking.background"];

    // 2.创建 NSURLSession
    NSOperationQueue *operationQueue = [NSOperationQueue mainQueue];

    NSURLSession *backgroundSession = [NSURLSession sessionWithConfiguration:backgroundConfiguration delegate:self delegateQueue:operationQueue];

    // 3.创建 NSURLSessionDownloadTask
    NSURLSessionDownloadTask *downloadTask = [backgroundSession downloadTaskWithURL:url];
    [downloadTask resume];

    # prama mark - Delegate
    - (void)URLSession:(NSURLSession *)session
    downloadTask:(NSURLSessionDownloadTask *)downloadTask
    didWriteData:(int64_t)bytesWritten
    totalBytesWritten:(int64_t)totalBytesWritten
    totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
    {
    NSLog(@"Session %@ download task %@ wrote an additional %lld bytes (total %lld bytes) out of an expected %lld bytes.n", session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
    }

    - (void)URLSession:(NSURLSession *)session
    downloadTask:(NSURLSessionDownloadTask *)downloadTask
    didResumeAtOffset:(int64_t)fileOffset
    expectedTotalBytes:(int64_t)expectedTotalBytes
    {
    NSLog(@"Session %@ download task %@ resumed at offset %lld bytes out of an expected %lld bytes.n", session, downloadTask, fileOffset, expectedTotalBytes);
    }

    - (void)URLSession:(NSURLSession *)session
    downloadTask:(NSURLSessionDownloadTask *)downloadTask
    didFinishDownloadingToURL:(NSURL *)location
    {
    NSLog(@"Session %@ download task %@ finished downloading to URL %@n", session, downloadTask, location);

    // Perform the completion handler for the current session
    self.completionHandlers[session.configuration.identifier]();

    // Open the downloaded file for reading
    NSError *readError = nil;
    NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingFromURL:location error:readError];
    // ...

    // Move the file to a new URL
    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSURL *cacheDirectory = [[fileManager URLsForDirectory:NSCachesDirectory inDomains:NSUserDomainMask] firstObject];
    NSError *moveError = nil;
    if ([fileManager moveItemAtURL:location toURL:cacheDirectory error:moveError]) {
    // ...
    }
    }

    三、Uploading Body Content

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    // 1.创建 NSURLSessionConfiguration
    NSURLSessionConfiguration *defaultConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];

    // 配置默认会话的缓存行为
    NSString *cachesDirectory = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject;
    NSString *cachePath = [cachesDirectory stringByAppendingPathComponent:@"MyCache"];

    /* Note:
    iOS需要设置相对路径:〜/Library/Caches
    OS X 要设置绝对路径。
    */
    NSURLCache *cache = [[NSURLCache alloc] initWithMemoryCapacity:16384
    diskCapacity:268435456
    diskPath:cachePath];
    defaultConfiguration.URLCache = cache;
    defaultConfiguration.requestCachePolicy = NSURLRequestUseProtocolCachePolicy;

    // 2.创建 NSURLSession
    NSOperationQueue *operationQueue = [NSOperationQueue mainQueue];

    NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration:defaultConfiguration delegate:self delegateQueue:operationQueue];

    // ***************************************************************
    // 3.1.上传 Data
    NSURL *textFileURL = [NSURL fileURLWithPath:@"/path/to/file.txt"];
    NSData *data = [NSData dataWithContentsOfURL:textFileURL];

    NSURL *url = [NSURL URLWithString:@"https://www.example.com/"];
    NSMutableURLRequest *mutableRequest = [NSMutableURLRequest requestWithURL:url];
    mutableRequest.HTTPMethod = @"POST";
    [mutableRequest setValue:[NSString stringWithFormat:@"%lld", data.length] forHTTPHeaderField:@"Content-Length"];
    [mutableRequest setValue:@"text/plain" forHTTPHeaderField:@"Content-Type"];

    NSURLSessionUploadTask *uploadTask = [defaultSession uploadTaskWithRequest:mutableRequest fromData:data];
    [uploadTask resume];

    // ***************************************************************
    // 3.2.上传 File
    NSURL *textFileURL = [NSURL fileURLWithPath:@"/path/to/file.txt"];

    NSURL *url = [NSURL URLWithString:@"https://www.example.com/"];
    NSMutableURLRequest *mutabl 大专栏  iOS 编程:NSURLSessioneRequest = [NSMutableURLRequest requestWithURL:url];
    mutableRequest.HTTPMethod = @"POST";

    NSURLSessionUploadTask *uploadTask = [defaultSession uploadTaskWithRequest:mutableRequest fromFile:textFileURL];
    [uploadTask resume];

    // ***************************************************************
    // 3.3.上传 Stream
    NSURL *textFileURL = [NSURL fileURLWithPath:@"/path/to/file.txt"];

    NSURL *url = [NSURL URLWithString:@"https://www.example.com/"];
    NSMutableURLRequest *mutableRequest = [NSMutableURLRequest requestWithURL:url];
    mutableRequest.HTTPMethod = @"POST";
    mutableRequest.HTTPBodyStream = [NSInputStream inputStreamWithFileAtPath:textFileURL.path];
    [mutableRequest setValue:@"text/plain" forHTTPHeaderField:@"Content-Type"];
    [mutableRequest setValue:[NSString stringWithFormat:@"%lld", data.length] forHTTPHeaderField:@"Content-Length"];

    NSURLSessionUploadTask *uploadTask = [defaultSession uploadTaskWithStreamedRequest:mutableRequest];
    [uploadTask resume];

    四、NSURLSessionDataTask 发送 GET 请求

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    - (void)p_NSURLSessionDataTask_GET {
    // 请求路径
    NSURL *url = [NSURL URLWithString:@"https://op.juhe.cn/shanghai/hospital?dtype=&key=123"];

    // 1.创建 NSURLSession,使用共享 Session
    NSURLSession *session = [NSURLSession sharedSession];

    // 2.创建 NSURLSessionDataTask, 默认 GET 请求
    NSURLSessionDataTask *dataTask = [session dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
    if (error) {
    NSLog(@"%@",error.localizedDescription);
    }else {
    NSLog(@"%@",data);
    }
    }];
    // 3.执行 Task
    [dataTask resume];
    }

    五、NSURLSessionDataTask 发送 POST 请求

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    - (void)p_NSURLSessionDataTask_POST {
    // 请求路径
    NSURL *url = [NSURL URLWithString:@"https://op.juhe.cn/shanghai/hospital"];

    // 创建请求对象
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    // 设置请求方法
    request.HTTPMethod = @"POST";
    // 设置请求体
    NSString *stringBody = @"dtype=&key=123";
    request.HTTPBody = [stringBody dataUsingEncoding:NSUTF8StringEncoding];

    // 1.创建 NSURLSession,使用共享 Session
    NSURLSession *session = [NSURLSession sharedSession];
    // 2.创建 NSURLSessionDataTask
    NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
    if (error) {
    // error
    }else {
    // data
    }
    }];
    // 3.执行 Task
    [dataTask resume];
    }

    六、NSURLSessionDataTask 设置代理发送请求

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    - (void)p_NSURLSessionDataTask_Delegate {
    // 请求路径
    NSURL *url = [NSURL URLWithString:@"https://op.juhe.cn/shanghai/hospital"];

    // 创建请求对象
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    // 设置请求方法
    request.HTTPMethod = @"POST";
    // 设置请求体
    NSString *stringBody = @"dtype=&key=5718abc3837ecb471c5d5b1ef1e35130";
    request.HTTPBody = [stringBody dataUsingEncoding:NSUTF8StringEncoding];

    // 1.创建 NSURLSessionConfiguration
    NSURLSessionConfiguration *configuration =
    [NSURLSessionConfiguration defaultSessionConfiguration];
    // 2.创建 NSURLSession
    NSURLSession *session =
    [NSURLSession sessionWithConfiguration:configuration
    delegate:self
    delegateQueue:nil];
    // 3.创建 NSURLSessionDataTask
    NSURLSessionDataTask *dataTask =
    [session dataTaskWithRequest:request];
    // 4.执行 Task
    [dataTask resume];
    }

    #pragma mark - NSURLSessionDelegate

    - (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(nullable NSError *)error {
    // 请求失败调用。
    }

    - (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
    completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler {
    // 处理身份验证和凭据。
    }

    - (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session {
    // 后台任务下载完成后调用
    }

    #pragma mark - NSURLSessionDataDelegate

    - (void)URLSession:(NSURLSession *)session
    dataTask:(NSURLSessionDataTask *)dataTask
    didReceiveResponse:(NSURLResponse *)response
    completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler {
    // 接收到服务器响应的时候调用
    // 默认情况下不接收数据,必须告诉系统是否接收服务器返回的数据
    completionHandler(NSURLSessionResponseAllow);
    }

    - (void)URLSession:(NSURLSession *)session
    task:(NSURLSessionTask *)task
    didCompleteWithError:(nullable NSError *)error {
    // 请求失败调用
    }

    - (void)URLSession:(NSURLSession *)session
    dataTask:(NSURLSessionDataTask *)dataTask
    didReceiveData:(NSData *)data {
    // 接受到服务器返回数据的时候调用,可能被调用多次
    }

    获取新闻示例代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    -(void)viewdidload {

    //创建NSURLSession对象
    NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
    _session = [NSURLSession sessionWithConfiguration:config
    delegate:nil
    delegateQueue:nil];
    //发起网络请求获取新闻
    [self fetchHrssnews];
    }

    #pragma 获取新闻方法
    - (void)fetchHrssnews {

    //创建NSURLRequest对象
    NSString *requestString = hrssnewsString;
    NSURL *url = [NSURL URLWithString:requestString];
    //方法参数:统一资源定位符、缓存策略:忽略本地缓存、等待web服务器响应最长时间
    NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url
    cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
    timeoutInterval:60.0f];
    //设置请求方式为POST
    [req setHTTPMethod: @"POST"];

    //设置请求体
    NSString *dataString = @"ksym=0&jsym=15";
    NSData *postData = [dataString dataUsingEncoding:NSUTF8StringEncoding];
    [req setHTTPBody:postData];

    //创建NSURLSessionDataTask对象
    NSURLSessionDataTask *dataTask = [self.session dataTaskWithRequest:req completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {

    //解析JSON数据
    NSDictionary *jsonObject = [NSJSONSerialization
    JSONObjectWithData:data
    options:kNilOptions
    error:nil];

    self.msgflag = jsonObject[@"msgflag"];
    HQLog(@"msgflag:%@",self.msgflag);
    self.msg = jsonObject[@"msg"];
    HQLog(@"msg:%@",self.msg);

    //判断是否成功获取服务器端数据
    if ([self.msgflag isEqualToString:@"0"]) {
    HQLog(@"msgflag:%@",self.msgflag);
    }else{
    HQLog(@"msgflag:-1,msg:For input string: ");
    }

    //使用dispatch_asynch函数让reloadData方法在主线程中运行
    dispatch_async(dispatch_get_main_queue(), ^{
    //重新加载UITableView对象的数据
    [self.tableView reloadData];});
    //停止刷新
    [self.tableView.mj_header endRefreshing];
    }];

    //NSURLSessionDataTask在刚创建的时候默认处于挂起状态,需要手动调用恢复。
    [dataTask resume];

    }

    参考

  • 相关阅读:
    磁盘 inodes 不足 Free inodes is less than 20% on volume
    记录一次Nginx使用第三方模块fair导致的线上故障排错
    xml 特殊字符 导致的 solr 数据导入异常
    Jenkins 定时备份插件 ThinBackup
    Elasticsearch 节点磁盘使用率过高,导致ES集群索引无副本
    Elasticsearch定时删除索引第二版
    js for in 获得遍历数组索引和对象属性
    js函数作用域
    django 1.11.1 连接MySQL
    angular 4 和django 1.11.1 前后端交互 总结
  • 原文地址:https://www.cnblogs.com/lijianming180/p/12041520.html
Copyright © 2020-2023  润新知