美文网首页
iOS_将网络数据存储到内存

iOS_将网络数据存储到内存

作者: Justin_S_Wang | 来源:发表于2019-06-28 08:57 被阅读0次

翻译原文链接

https://developer.apple.com/documentation/foundation/url_loading_system/fetching_website_data_into_memory

通过从URL会话创建数据任务将获取到的数据直接存入内存

概述

对于比较小的一些和远程服务器的交互,你可以通过使用URLSessionDataTask来获取数据并存入内存(而不是使用URLSessionDownloadTask类,用来将数据直接存入文件系统)。一个数据任务通常被认为使用来对网络服务器进行请求。

你是用一个URL会话实体来创建一个任务。如果你的需求相当的简单,你可以使用URLSession类的shared实体。如果你想通过代理回调来和数据转换进行交互的话,你将需要创建一个会话而不是使用shared实体。当创建一个会话时你可以使用URLSessionConfiguration实例,同时传入一个实现了URLSessionDelegate或其子协议的类达到该目的。会话可以被重新使用来创建多个任务,所以,你可以对每一个不同的配置需求,创建一个会话并保存起来。

注意

注意不要创建比你所需要更多的会话。举个例子,如果你的app不同部分需要使用类似配置的会话,那就创建一个会话并共享

一旦你有了一个会话,你就可以通过dataTask()方法来创建一个任务。任务在创建的时候是处于挂起的状态,可以通过resume()方法来开启任务。

通过Completion Handler来接收结果

获取数据最简单的方法是创建一个带有completion handler的数据任务。按照这个约定,这个任务将会在你所提供的completion handler中传递服务器的回应,数据和可能出现的错误。下图展示了一个会话和一个任务之间的关系以及结果是如何传递给completion handler的。

figure_1

要创建一个带有completion handler的数据任务,要调用URLSessiondataTask(with:)的方法。你的completion handler需要做以下三件事:

  1. 验证error参数是nil。如果不是的话,将会发生传输错误;就要处理错误并退出。
  2. 检查response参数来验证状态码是成功而且MIME类型是期待的类型。如果不是这样的话,就要处理服务器错误并退出。
  3. 按需使用data实例。

以下代码展示了获取URL内容的startLoad()方法。一开始,它通过URLSession类的shared实例来创建数据任务并将结果传递给completion handler。再检查完本地和服务器错误后,这个handler将数据转换成字符串,并用它来填充WKWebView控件,当然,你的app可能将获取到的数据用于其他用处,像它转换成一个数据模型。


func startLoad() {
   let url = URL(string: "https://www.example.com/")!
   let task = URLSession.shared.dataTask(with: url) { data, response, error in
       if let error = error {
           self.handleClient(error)
           return
       }
       guard let httpResponse = response as? HTTPURLResponse, 
           (200...299).contains(httpResponse.statusCode) else {
           self.handleServerError(response)
           return
       }
       if let mimeType = httpResponse.mimeType, mimeType == "text/html", 
           let data = data,
           let string = String(data: data, encoding: .utf8) {
               DispatchQueue.main.async {
                   self.webView.loadHTMLString(string, baseURL: url)
               }
           }
   }
   task.resume()
}

重点

Completion Handler是在另一个GCD队列而不是创建任务的队列中被调用。所以,任何使用dataerror来更新界面的工作--像更新webview--应该明确被放到主线程来做就像这里处理的一样。

通过代理来接收传递的详细信息和结果

当创建数据任务时,想要更进一步获得数据任务执行时的活动状态,你可以对session设置一个代理,而不是comppletion handler。如下图:

figure_2

通过这种方式,数据部分将通过URLSessionDelegate的方法urlSession(:dataTask:didReceive:)在他们返回时提供,直到传输结束或者传输失败时终止。在传输过程中,代理也会接受其他种类的事件。

当你想使用代理这种方法的时候你要创建你自己的URLSession实例,而不是使用URLSession类的简单的shared实例。创建一个新的session将允许你把session的代理设为你自己的类。代码如下。

声明并将自己创建的类实现一个或多个代理协议(URLSessionDelegate,URLSessionTaskDelegate,URLSessionDataDelegateURLSessionDownloadDelegate)。然后通过初始化方法init(configuration:delegate:delegateQueue:)来创建URLSession实例。你可以通过这个初始化方法来自定义配置该实例。将waitsForConnectivity设为true是个很好的例子,这样的话,会话就会等待到合适的连接,而不是在需要的连接不可用时直接失败。


private lazy var session: URLSession = {
    let configuration = URLConfiguration.default
    configuration.waitsForConnectivity = true
    return URLSession(configuration: configuration, delegate: self, delegateQueue: nil)
}

接下来一段代码将展示startLoad()方法,它使用上面的这个会话来开启一个数据任务,并且使用代理回调来处理接受数据和错误。这段代码会实现三个代理回调:

  1. urlSession(_:dataTask:didReceive:completionHandler:)用来验证服务器回应带有成功的HTTP状态码,并且MIME类型为text/html或者text/plain,如果两者都不是,这个任务将会被取消;反之,它将继续执行。

  2. urlSession(_:dataTask:didReceive:)将接受任务中的每个数据块并将它拼接到叫做receivedData的这个缓冲区中。

  3. urlSession(_:task:didCompleteWithError:)首先查看是否发生了传输层的错误。如果没有,它将试图将receivedData缓冲块转换为字符串并将它设置为webView的内容。


var receivedData: Data?

func startLoad() {
    loadButton.isEnabled = false
    let url = URL(string: "http://www.example.com/")!
    receivedData = Data()
    let task = session.dataTask(with: url)
    task.resume()
}

// delegate methods

func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, 
completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) {
    guard let response = response as? HTTPURLResponse,
    (200...299).contains(response.statusCode),
    let mimeType = response.mimeType,
    mimeType == "text/html" else {
        completionHandler(.cancel)
        return
    }
    completionHandler(.allow)
}

func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
    self.receivedData?.append(data)
}

func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
    DispatchQueue.main.async {
        self.loadButton.isEnabled = true
        if let error = error {
            handleClientError(error)
        } else if let receivedData = self.receivedData,
            let string = String(data: receivedData, encoding: .utf8) {
            self.webView.loadHTMLString(string, baseURL: task.currentResponse?.url)
        }
    }
}

代理协议所提供的内容不仅限于以上代码,例如处理信任签名,重定向,和其他的情况。在URLSession的讨论中,再Using a URL Session中描述了在传输过程中可能发生的各种回调。

更多可参看

  • URLSession 一个协调一组网络传输任务相关的类
  • URLSessionTask 一个任务,像下载一个特殊的资源,执行一个URL会话

相关文章

  • iOS_将网络数据存储到内存

    翻译原文链接 https://developer.apple.com/documentation/foundati...

  • OC基础(六)——内存管理

    内存管理概述 内存管理内存的作用:存储数据. 如何将数据存储到内存之中.声明1个变量.然后将数据存储进去. 当数据...

  • 27.2、数据缓存 data cache

    将数据存储到浏览器内存作为数据缓存

  • 性能优化

    提升页面加载速度 减少网络请求 将请求过的数据存储到本地 redux、内存、sessionStorage等 通过函...

  • IO流——节点流

    定义 输入Input:读取外部数据(磁盘,光盘等存储设备的数据)到程序(内存)中。 输出output:将程序(内存...

  • 3-1变量和常量概述

    计算机在运行时用到的所有数据都被存储在内存中。 数据是各种各样的,因此,系统将数据存储到内存时会根据数据的类型为...

  • 2019-02-12 Octave移动数据

    将dat文件加载到内存同名矩阵中: 查看当前Octave内存中所存储的所有变量: 存储数据到硬盘: 以上是存成了二...

  • redis的rdb和aof的详细介绍

    redis的俩种持久化存储 rdb存储就是在不同的时间点,将内存中的数据生成快照并存储到硬盘介质上,通常数据存储是...

  • 数据持久化之NSUserDefaults

    所谓的数据持久化、就是将内存中的数据模型转换为存储模型,以及将存储模型转换为内存中的数据模型的统称. 数据模型可以...

  • 数据持久化

    数据持久化(PO)就是将内存中的数据模型转换为存储模型,以及将存储模型转换为内存中的数据模型的统称.数据模型可以是...

网友评论

      本文标题:iOS_将网络数据存储到内存

      本文链接:https://www.haomeiwen.com/subject/xibjcctx.html