Objective-C 编程:NSURLSession

The URL Loading System

The URL Loading System 描述了 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 跟踪而储存在用户本地终端上的数据(通常经过加密)。

Session 会话的概念

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

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

Session 会话类型

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

注:NSURLSession 使用完成之后需要释放,否则会引起内存泄漏问题。

NSURLSession 的优势:

  • NSURLSession 支持 HTTP 2.0 传输协议;
  • 处理下载任务时,可以直接把数据下载并保存到磁盘;
  • 支持后台下载和上传;
  • 同一个 session 发送多个请求,只需要建立一次连接(复用了 TCP);
  • 提供了全局的 session 并且可以统一配置,使用更加方便;
  • 下载的时候是多线程异步处理,效率更高;

task 任务类型

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

类的层级结构

注:

  • NSURLSessionTask 是一个抽象类,具体使用的是它的子类对象。
  • NSURLSessionDataTask 可以用来处理一般的网络请求,如 GET、POST 等。
  • NSURLSessionUploadTask 用于处理上传请求。
  • NSURLSessionDownloadTask 用于处理下载请求。
任务类型 描述
数据任务
(data tasks)
使用 NSData 对象发送和接收数据。处理应用程序与服务器之间的
简短的,经常交互的请求。 数据任务可以在每次接收到数据后就
返回,或者通过 completion handler 一次性返回所有数据到您的
应用程序。
下载任务
(download tasks)
以文件的形式检索数据,并在应用程序未运行时支持后台下载。
上传任务
(upload tasks)
以文件的形式发送数据,并在应用程序未运行时支持后台上传。
流任务
(stream tasks)
WWDC2015 新增,提供 TCP/IP 连接接口。

NSURLSessionDelegate 委托协议

委托协议的层级结构

后台传输注意事项

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

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

网络请求创建流程

graph LR
NSURL-->NSURLRequest
NSURLRequest-->NSURLSessionTask
NSURLSessionConfiguration-->NSURLSession
NSURLSession-->NSURLSessionTask
创建网络请求对象的流程图

描述:

  1. 首先创建并初始化 NSURLSessionConfiguration 对象,进行 Session 会话配置;
  2. 通过 NSURLSessionConfiguration 对象来创建并初始化 NSURLSession 对象;
  3. 创建 NSURL 对象,即网络请求路径,通过 NSURL 对象创建 NSURLRequest 对象,创建一个请求对象;
  4. 最后,基于之前创建的 NSURLSession 对象和 NSURLRequest 对象创建 NSURLSessionTask 对象;

创建 NSURLSessionConfiguration 对象

NSURLSessionConfiguration 类的参数可以设置网络配置信息,其包含了 cookie,安全和高速缓存策略,最大主机连接数,资源管理,网络超时等配置。

创建 NSURLSessionConfiguration 会话配置对象有三种方式,配置类型根据你想创建的 Session 会话的类型匹配创建。

1. 默认会话配置

类似 NSURLConnection 的标准配置,用硬盘来缓存数据。

NSURLSessionConfiguration *defaultConfig = [NSURLSessionConfiguration defaultSessionConfiguration];

2. 短暂会话配置

临时的进程内会话(内存),不会将 cookie、缓存(caches)和证书(credentials)储存到本地,只会放到内存中,当应用程序退出后数据也会消失,可以用于实现类似于 Safari 浏览器的 “无痕浏览模式”。

NSURLSessionConfiguration *ephemeralConfig = [NSURLSessionConfiguration ephemeralSessionConfiguration];

3. 后台会话配置

建立后台会话可以在应用程序挂起、退出、崩溃的情况下运行上传和下载任务,后台另起一个线程。另外,系统会根据设备的负载程度决定分配下载的资源,因此这种任务可能会很慢甚至超时失败。

NSURLSessionConfiguration *backgroundConfig = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"com.Apple.BackgroundDownload.BackgroundSession"];

其他配置策略

HTTPAdditionalHeaders 设置额外的请求头

默认为空,NSURLRequest 附件的请求头。这个属性会给所有使用该 configuration 的 session 生成的 tasks 中的 NSURLRequest 添加额外的请求头。

如果这里设置的请求头跟 NSURLRequest 中重复了,则优先使用 NSURLRequest 中配置请求头。

configuration.HTTPAdditionalHeaders = @{
    @"Accept": @"application/json",   // 可接受的数据类型
    @"Accept-Language": @"en",
    @"Authorization": 授权信息,
    @"User-Agent": 用户代理信息
};

networkServiceType 指定网络传输类型

精切地指出传输类型,可以让系统快速响应,提高传输质量,延长电池寿命等。
可选的枚举类型:

NSURLNetworkServiceTypeDefault  // 普通网络传输,默认值
NSURLNetworkServiceTypeVoIP  // 网络语音通信传输,只能在 VoIP 使用
NSURLNetworkServiceTypeVideo  // 视频传输
NSURLNetworkServiceTypeBackground  // 后台传输,优先级不高时可使用。对用户不需要的网络操作可使用
NSURLNetworkServiceTypeVoice  // 语音传输

allowsCellularAccess 允许蜂窝访问

是否允许使用蜂窝网络,默认值为 YES

timeoutIntervalForRequest 请求的超时时长

给 request 指定每次接收数据超时间隔,如果下一次接受新数据用时超过该值,则发送一个请求超时给该 request。默认为 60s

requestCachePolicy 缓存策略

缓存策略也可以通过 NSURLRequestrequestWithURL:cachePolicy:timeoutInterval: 方法设置。无论使用哪种缓存策略,都会在本地缓存数据

NSURLRequestCachePolicy 是一个枚举类型:

 typedef NS_ENUM(NSUInteger, NSURLRequestCachePolicy)
 {
     // 默认的缓存策略,使用协议的缓存策略
     NSURLRequestUseProtocolCachePolicy = 0,
 
     // 忽略本地缓存,每次都从网络加载
     NSURLRequestReloadIgnoringLocalCacheData = 1,
    
     // 忽略本地和远程的缓存数据
     NSURLRequestReloadIgnoringLocalAndRemoteCacheData = 4,
     
     // 忽略本地缓存,每次都从网络加载
     NSURLRequestReloadIgnoringCacheData = NSURLRequestReloadIgnoringLocalCacheData,

     // 返回缓存否则加载,很少使用
     NSURLRequestReturnCacheDataElseLoad = 2,
     
     // 只返回缓存,没有也不加载,很少使用
     NSURLRequestReturnCacheDataDontLoad = 3,

     // 重新加载重新验证缓存数据
     NSURLRequestReloadRevalidatingCacheData = 5,
 };

详情参见:NSURLSessionConfiguration API详解

创建和配置 Session

创建 NSURLSession 对象的三种方法:

1. 单例类会话,便捷创建方法

NSURLSession *sharedSession = [NSURLSession sharedSession];

2. sessionWithConfiguration: 方法

一般用 Completion Handle 块处理服务器返回的数据时时使用。

// 默认会话
NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration:defaultConfig];

// 短暂会话
NSURLSession *ephemeralSesson = [NSURLSession sessionWithConfiguration:ephemeralConfig];

// 后台会话
NSURLSession *backgroundSession = [NSURLSession sessionWithConfiguration:backgroundConfig];

3. sessionWithConfiguration:delegate:delegateQueue: 方法

需要遵守并实现委托协议或者指定线程的创建方法。

// 队列,如果该参数传递 nil 那么默认在子线程中执行
NSOperationQueue *operationQueue = [NSOperationQueue mainQueue];

// 默认会话
NSURLSession *defalutSession1 = [NSURLSession sessionWithConfiguration:defaultConfig delegate:self delegateQueue:operationQueue];

// 短暂会话
NSURLSession *ephemeralSesson1 = [NSURLSession sessionWithConfiguration:ephemeralConfig delegate:self delegateQueue:operationQueue];

// 后台会话
NSURLSession *backgroundSession1 = [NSURLSession sessionWithConfiguration:backgroundConfig delegate:self delegateQueue:operationQueue];

示例

一、NSURLSessionDataTask 示例

// MARK: 1.创建 NSURLSessionConfiguration 对象,进行 Session 会话配置
// 默认会话配置
NSURLSessionConfiguration *defaultConfig = [NSURLSessionConfiguration defaultSessionConfiguration];

// MARK: 2.配置默认会话的缓存行为
// Caches 目录:NSCachesDirectory
NSString *cachesDirectory = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject;
// 在 Caches 目录下创建创建子目录
NSString *cachePath = [cachesDirectory stringByAppendingPathComponent:@"MyCache"];
/*
 Note:
 iOS 需要设置相对路径:〜/Library/Caches
 OS X 要设置绝对路径。
 */
NSURLCache *cache = [[NSURLCache alloc] initWithMemoryCapacity:16384
                                                  diskCapacity:268435456
                                                      diskPath:cachePath];
defaultConfig.URLCache = cache;
defaultConfig.requestCachePolicy = NSURLRequestUseProtocolCachePolicy;

// MARK: 3.创建 NSURLSession 对象
NSOperationQueue *operationQueue = [NSOperationQueue mainQueue];

NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration:defaultConfig
                                                             delegate:nil
                                                        delegateQueue:operationQueue];

// MARK: 4.创建 NSURLSessionTask
NSURL *url = [NSURL URLWithString:@"https://www.baidu.com/"];
NSURLSessionTask *sessionTask = [defaultSession dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
    // 响应对象是一个 NSHTTPURLResponse 对象实例
    NSLog(@"Got response %@ with error %@.\n", response, error);
    NSLog(@"默认会话返回数据:\n%@ \nEND DATA\n", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
}];

// MARK: 开启任务
[sessionTask resume];

二、NSURLSessionDownloadTask 示例

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]) {
        // ...
    }
}

三、NSURLSessionUploadTask 示例

// 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 *mutableRequest = [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];

使用 NSURLSession 上传文件主要步骤及注意点

主要步骤
  1. 确定上传请求的路径( NSURL )
  2. 创建可变的请求对象( NSMutableURLRequest )
  3. 修改请求方法为 POST
  4. 设置请求头信息(告知服务器端这是一个文件上传请求)
  5. 按照固定的格式拼接要上传的文件等参数
  6. 根据请求对象创建会话对象( NSURLSession 对象)
  7. 根据 session 对象来创建一个 uploadTask 上传请求任务
  8. 执行该上传请求任务(调用 resume 方法)
  9. 得到服务器返回的数据,解析数据(上传成功 | 上传失败)
注意点
  1. 创建可变的请求对象,因为需要修改请求方法为 POST,设置请求头信息
  2. 设置请求头这个步骤可能会被遗漏
  3. 要处理上传参数的时候,一定要按照固定的格式来进行拼接
  4. 需要采用合适的方法来获得上传文件的二进制数据类型( MIMEType )
MIMEType 类型

MIME (Multipurpose Internet Mail Extensions) 是描述消息内容类型的因特网标准。

MIME 消息能包含文本、图像、音频、视频以及其他应用程序专用的数据。

参考:MIME 参考手册

四、NSURLSessionDataTask 发送 GET 请求

发送 GET 请求会使用 NSURLSessionDataTaskdataTaskWithURL: completionHandler: 方法。

// MARK: 1.封装请求路径,查询 IP 地址
// 接口来源:<https://dog.ceo/dog-api/>
NSURL *url = [NSURL URLWithString:@"https://dog.ceo/api/breeds/image/random"];

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

// MARK: 3.创建 NSURLSessionDataTask, 默认 GET 请求
NSURLSessionDataTask *dataTask = [session dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
    if (error) {
        NSLog(@"%@",error.localizedDescription);
    }else {
        
        // !!!: 主线程执行 UI 更新
        dispatch_async(dispatch_get_main_queue(), ^{
            // data
        });
    }
}];

// MARK: 4.执行任务
[dataTask resume];

五、NSURLSessionDataTask 发送 POST 请求

发送 POST 请求会使用 NSURLSessionDataTaskdataTaskWithRequest: completionHandler: 方法。

// 封装请求路径
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 {
         // 获得数据后,返回到主线程更新 UI
         dispatch_async(dispatch_get_main_queue(), ^{
             NSString *responseString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
             self.dataLabel.text = responseString;
         });
     }
}];
// 3.执行 Task
[dataTask resume];

六、NSURLSessionDataTask 设置代理发送请求

之前的请求获得的数据是直接在 completionHandler: Block 块中处理的,你也可以通过遵守 Delegate 代理的方式获得返回的数据。

- (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 {
    // 接受到服务器返回数据的时候调用,可能被调用多次
}

七、获取新闻示例代码:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
    
    //创建NSURLSession对象
    NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
    _session = [NSURLSession sessionWithConfiguration:config
                                             delegate:nil
                                        delegateQueue:nil];
    //发起网络请求获取新闻
    [self fetchHrssnews];
}


#pragma mark - 获取新闻数据

- (void)fetchHrssnews {
    
    // 创建NSURLRequest对象
    NSString *requestString = @"https://example.com/api/v1/news";
    NSURL *url = [NSURL URLWithString:requestString];
    
    /**
     方法参数
     
     * 统一资源定位符:接口 URL 地址
     * 缓存策略:忽略本地缓存、
     * 等待 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];
        // msgflag 是业务状态码,0 表示响应正常,1 表示响应错误。
        NSString *msgflag = jsonObject[@"msgflag"];
        // msg 是返回的具体业务数据
        __unused NSString *msg = jsonObject[@"msg"];
        
        //判断是否成功获取服务器端数据
        if ([msgflag isEqualToString:@"0"]) {
            // 返回数据成功,继续
        }else{
            // 返回数据错误,提示用户
        }
        
        // 使用 dispatch_asynch 函数让 reloadData 方法在主线程中运行
        dispatch_async(dispatch_get_main_queue(), ^{
            //重新加载UITableView对象的数据
            // [self.tableView reloadData];});
            //停止刷新
            // [self.tableView.mj_header endRefreshing];
        });
    }];
    
    // NSURLSessionDataTask 在刚创建的时候默认处于挂起状态,需要手动调用恢复。
    [dataTask resume];
}

参考

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 224,289评论 6 522
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 95,968评论 3 402
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 171,336评论 0 366
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 60,718评论 1 300
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 69,734评论 6 399
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 53,240评论 1 314
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 41,631评论 3 428
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 40,599评论 0 279
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 47,139评论 1 324
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 39,166评论 3 345
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 41,286评论 1 354
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,917评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,604评论 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 33,075评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 34,205评论 1 275
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 49,814评论 3 381
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 46,351评论 2 365

推荐阅读更多精彩内容