记录寄己走过的路

iOS NSURLSessionApple译文

Write in the first【写在最前】


由于 X X (自动脑补) 的苹果在 iOS9 之后已经废弃了 NSURLConnection,所以在现在的实际开发中,除了大家常见的 AFN 框架,一般使用的是 iOS7 之后推出的 NSURLSession,作为一名 iOS 开发者,如果你只知道 AFN 框架来进行网络请求,那就 X x 了。

在「时间 & 知识 」有限内,总结的文章难免有「未全、不足 」的地方,还望各位好友指出,以提高文章质量。



正题

在这里首先附上官方原文地址 URLSession Class

Relationships
Inherits From NSObject 继承自: NSObject
Conforms To CVarArg,Equatable,Hashable
Framework iOS 7.0+

Class
URLSession

The NSURLSession class and related classes provide an API for downloading content. This API provides a rich set of delegate methods for supporting authentication and gives your app the ability to perform background downloads when your app is not running or, in iOS, while your app is suspended.

NSURLSession 类及其相关类为下载内容提供了接口。这个 API 提供了一系列丰富的代理方法来支持授权,而且让你的 APP 在后台被挂起时也能继续下载。

Overview

The URLSession class natively supports the data, file, ftp, http, and https URL schemes, with transparent support for proxy servers and SOCKS gateways, as configured in the user’s system preferences.

URLSession supports the HTTP/1.1, SPDY, and HTTP/2 protocols. HTTP/2 support is described by RFC 7540, and requires a server supporting either ALPN or NPN for protocol negotiation.

You can also add support for your own custom networking protocols and URL schemes (for your app’s private use) using URLProtocol.

通过代理服务器的和 SOCKS 网关为用户配置好系统设置,NSURLSession类完全支持 data, file, ftp, http, https类型的超链接。你也可以添加一些支持你自己定制的网络协议或超链接(专门为你自己的 APP 所用)

Important
The NSURLSession API involves many different classes working together in a fairly complex way that may not be obvious if you read the reference documentation by itself. Before using this API, you should read URL Session Programming Guide to gain a conceptual understanding of how these classes interact with one another.

重要:
NSURLSession 的 API 有很多你在看文档时可能感觉并不重要,但实际上却以很复杂的方式来共同发挥作用的类。用这个 API 之前,你应该看一下 URL Session Programming Guide 来了解这些类之间相互调用的一些概念。

With the NSURLSession API, your app creates one or more sessions, each of which coordinates a group of related data transfer tasks. For example, if you are writing a web browser, your app might create one session per tab or window, or one session for interactive use and another session for background downloads. Within each session, your app adds a series of tasks, each of which represents a request for a specific URL (following HTTP redirects if necessary).

通过 NSURLSession 的 API,你的 APP可以创建一个或者多个事务,其中的每一个事务都用来协助一组相关的数据来传输任务。比如,如果你在写一个浏览器,你的 APP 可能在每一栏或是每一个窗口都创建了一个事务,可能一个用来进行交互,一个用来后台下载。在每一个事务中,你的 APP 都添加了一些了的任务,其中的每一个任务就代表一个请求或是一个特定的 URL(必要时进行HTTP重定向)

The tasks within a given URL session share a common session configuration object, which defines connection behavior, such as the maximum number of simultaneous connections to make to a single host, whether to allow connections over a cellular network, and so on. The behavior of a session is determined in part by which method you call when creating its configuration object:

  • The singleton shared session (which has no configuration object) is for basic requests. It is not as customizable as sessions that you create, but it serves as a good starting point if you have very limited requirements. You access this session by calling the shared class method. See that method’s discussion for more information about its limitations.
  • Default sessions behave much like the shared session (unless you customize them further), but let you obtain data incrementally using a delegate. You can create a default session configuration by calling the default method on the URLSessionConfiguration class.
  • Ephemeral sessions are similar to default sessions, but do not write caches, cookies, or credentials to disk. You can create an ephemeral session configuration by calling the ephemeral method on the URLSessionConfiguration class.
  • Background sessions let you perform uploads and downloads of content in the background while your app is not running. You can create a background session configuration by calling the backgroundSessionConfiguration(_:) method on the URLSessionConfiguration class.

给定 URL事务的这些任务共享一个事务配置 session,这个对象定义好了连接行为,比如通往同一个主机的相似连接的最大连接数,不管是通过蜂窝移动网络还是别的方式来允许连接。session 的行为一部分是由创建这个配置对象时你调用的方法来决定:

  • 单例 shared session (没有配置对象)用于普通请求。它像你自己创建的session一样可定制,但是如果你的请求有限的话它是一个很好的起点,通过调用 sharedSession 来获取这个 session。看这个方法的文档来得到它的一些限制信息。

  • Default sessions 的行为和 shared session 差不多(除非你自己进行了深度定制),但你必须要通过代理方法得到增量数据。调用 NSURLSessionConfiguration 中的 defaultSessionConfiguration 方法来创建 Default session。

  • Ephemeral sessions和Default sessions 很相似,但是不写入 caches,cookies, 或credentials。
    调用 NSURLSessionConfiguration 中的 ephemeral Session Configuration 方法来创建 ephemeral session。

  • Background sessions 可以在你的 APP 在后台被挂起时进行上传或下载。调用 NSURLSessionConfiguration 中的 backgroundSessionConfiguration: 方法来创建 background session。

The session configuration object also contains a reference to URL cache and cookie storage objects that may be used when making requests and handling the responses, depending on the configuration and request type.

session 配置对象还包含了对 URL cache 和 cookie 存储对象发起请求并处理相应的引用,具体取决于配置和请求类型。

The tasks in a session also share a common delegate that lets you provide and obtain information when various events occur—when authentication fails, when data arrives from the server, when data is ready to be cached, and so on. For all background downloads and uploads, you must provide a delegate that conforms to the URLSessionDownloadDelegateObjective-C protocol. Otherwise, if you don’t need any of the features provided by a delegate, you can use this API without providing a delegate by passing nil when you create a session.

当多种类型的事件发生时(比如授权失败时,数据传输到服务器时,数据准备好缓存时等等)session 中的任务也会共享一个通用的代理来让你提供和获取相关信息。对于所有的后台下载和上传,你必须提供一个遵从 NSURLSessionDownloadDelegate的 OC 协议。否则,如果你不需要任何由代理提供的特性,在你创建 session 时就给 delegate 传 nil 来调用这个 API。

Important
The session object keeps a strong reference to the delegate until your app exits or explicitly invalidates the session. If you do not invalidate the session, your app leaks memory until it exits.

重要:
session 对你的 delegate 始终保持强引用,除非你的 app 退出或者 session已明确失效。如果你没有使 session 失效,你的 app 将会内存溢出,直到它终止。

Within a session, you create tasks that optionally upload data to a server, then retrieve data from the server either as a file on disk or as one or more NSData objects in memory. The NSURLSession API provides three types of tasks:

  • Data tasks send and receive data using NSData objects. Data tasks are intended for short, often interactive requests to a server.
  • Upload tasks are similar to data tasks, but they also send data (often in the form of a file), and support background uploads while the app is not running.
  • Download tasks retrieve data in the form of a file, and support background downloads and uploads while the app is not running.

session 中,你创建了可上传数据到服务器的任务,然后通过磁盘上的文件,或是内存中的一个或多个 NSData 对象来获取数据。 NSURLSession 的 API 提供了三种类型的任务:

  • data tasks 通过 NSData 对象来发送和接受数据。 data tasks 用于与服务器进行简短频繁的请求。
  • Upload tasksdata tasks 类似,但是他们也可以发送数据(一般是通过文件的形式),也支持 app 后台上传。
  • Download tasks 通过文件的形式来获取数据,并且支持 app 后台上传和下载。

Like most networking APIs, the NSURLSession API is highly asynchronous. It returns data to your app in one of two ways, depending on the methods you call:

  • To a completion handler block that is called when a transfer finishes successfully or with an error.
  • By calling methods on the session’s delegate as data is received and when the transfer is complete.

和大多数网络接口一样,NSURLSession是高度异步的。它通过两种方式中的其中一种来返回数据给 app,取决于你调用的方法:

  • 当传输成功或是发生错误时调用 completion handler block
  • 当收到数据或是传输完成时调用 session 的代理方法

In addition to delivering this information to delegates, the NSURLSession API provides status and progress properties that you can query if you need to make programmatic decisions based on the current state of the task (with the caveat that its state can change at any time).

URL sessions also support canceling, restarting or resuming, and suspending tasks, and provide the ability to resume suspended, canceled, or failed downloads where they left off.

除了通过代理来传递数据,如果你需要通过当前任务的状态(给出一些状态警示)做一些程序上的决定, NSURLSession的 API 统一提供了状态和进度的属性,方便查询。

URL sessions 同样支持取消,重启,恢复,挂起任务,而且可以在他们停止的地方恢复挂起、取消、失败了的下载。

URL Session Class Hierarchy
层级结构
The NSURLSession API consists of the following classes (nested to show subclass relationships):
NSURLSession API 由下面的类组成(下面的缩进显示的是子类的关系)

  • URLSession—A session object. (一个 session 对象)
  • URLSessionConfiguration—A configuration object used when initializing the session.(用来初始化 session 的配置对象)
  • URLSessionTask—The base class for tasks within a session.(在 session 中表示任务的基类)
    • URLSessionDataTask—A task for retrieving the contents of a URL as an NSData object.(用来获取 NSData 对象的 URL 的任务)
      • URLSessionUploadTask—A task for uploading a file, then retrieving the contents of a URL as an NSData object.(用来上传文件,并接收 NSData 对象的 URL 的任务)
    • URLSessionDownloadTask—A task for retrieving the contents of a URL as a temporary file on disk(以磁盘临时文件的形式获取 URL 的内容的任务)
    • URLSessionStreamTask—A task for establishing a TCP/IP connection(用来建立 TCP/IP连接的任务)

In addition, the NSURLSession API provides four protocols that define delegate methods your app can implement to provide more fine-grained control over session and task behavior.

除此之外,NSURLSession 还提供了4个代理, 订了代理方法,你的 app 可以实现这些代理方法,提供对 session 和 task 更加细化的控制。

  • URLSessionDelegate—Defines delegate methods to handle session-level events(可处理 session level 事件的代理)
  • URLSessionTaskDelegate—Defines delegate methods to handle task-level events common to all task types(可处理和其他所有 task 类型相通的 task level 的方法的代理)
  • URLSessionDataDelegate—Defines delegate methods to handle task-level events specific to data and upload tasks(可处理与数据和上传任务相关的 task level 的方法的代理)
  • URLSessionDownloadDelegate—Defines delegate methods to handle task-level events specific to download tasks(处理与下载任务相关的 task level 的方法的代理)
  • URLSessionStreamDelegate—Defines delegate methods to handle task-level events specific to stream tasks(处理与数据流相关的 task level 的方法的代理)

Finally, the NSURLSession API uses a number of classes that are also commonly used with other APIs such as NSURLConnection and NSURLDownload. Some of these shared classes include:

最终,NSURLSession 的 API 使用了很多类,这些类也同样使用了其他的 API,比如 NSURLConnectionNSURLDownload。共同使用的类包括:

  • NSURL—An object that contains a URL.(包含了 URL 的对象)
  • NSURLRequest—Encapsulates metadata related to a URL request, including the URL, request method, and so on.(封装与 URL 请求,包括 URL、 请求方法等等相关的元数据)
  • NSURLResponse—Encapsulates metadata related to a server’s response to a request, such as the content MIME type and length. (封装服务器相应的请求,如与 Content 的 MIME 类型和长度相关的元数据)
    • NSHTTPURLResponse—Adds additional metadata specific to HTTP requests, such as response headers.(给 HTTP请求添加特定的附加元数据,比如 response headers)
  • NSCachedURLResponse—Encapsulates an URLResponse object, along with the actual body data of the server’s response, for caching purposes.(用来缓存服务器响应的 body data 封装的一个NSURLResponse对象)

Authentication and TLS Customization
身份验证和 TLS 定制

When a server requests authentication or provides credentials during TLS negotiation, the URL session calls methods on its delegate, allowing you to handle the authentication or certificate validation in a custom manner. The method it calls depends on whether you are handling a task-specific challenge or a session-wide challenge. Table 1 shows the difference between the two.

当服务器要求身份验证或在 TLS 协商期间提供凭证时,URL Session 通过调用代理方法,让你可以自定义处理身份验证或证书验证。
调用的代理方法取决于你是在处理 task 相关还是 session范围内的挑战。Table 1 给出了两者之间的区别。

Table 1
Session-level and connection-level challenges

Session-wide challenges Task-specific challenges
NSURLAuthenticationMethodNTLM NSURLAuthenticationMethodDefault
NSURLAuthenticationMethodNegotiate NSURLAuthenticationMethodHTTPBasic
NSURLAuthenticationMethodClientCertificate NSURLAuthenticationMethodHTTPDigest
NSURLAuthenticationMethodServerTrust -

对于 task 相关的挑战, session 会调用代理的
URLSession:task:didReceiveChallenge:completionHandler: 方法。

对于 session 相关的授权挑战,session 会调用代理的
URLSession:didReceiveChallenge:completionHandler: 方法,如果它不存在,则调用代理的URLSession:task:didReceiveChallenge:completionHandler: 方法。

If you do not implement these methods, when a request requires client authentication, the URL session attempts to authenticate as follows:

  • Using the authentication information provided as part of the requested URL, if available
  • By looking up Internet passwords and certificates in the user’s keychain (in macOS) or the app’s keychain (in iOS)

当请求需要客户端授权时,如果你没有实现这些方法,URL Session 会通过下面的方式来尝试授权:

  • 如果 URL中含有授权信息的话,通过请求的 URL 中的授权信息。
  • 在用户的钥匙串( 在 OS X 中)或是 app 的钥匙串(在 iOS 中)寻找网络密码或是证书。

Then, if credentials are not available, or if the server rejects the credentials, the connection continues without authenticating. For HTTP and HTTPS requests, the connection attempt fails with an appropriate HTTP status code, and may provide alternative content (such as the public version of a private site). For other URL types (such as FTP),the connection fails with a connection failure.

如果验证不可用,或是服务器拒绝了验证,那这个连接将会在没有授权的情况下继续。对于 HTTP 和 HTTPs 请求来说,连接会通过一个对应的 HTTP 状态码反应尝试失败, 也可能会提供可选择的内容(比如私有网站的公开版本)。对于其他类型的 URL(比如 FTP),将会直接连接失败。

Note
Kerberos authentication is handled transparently. The delegate methods described here do not apply to Kerberos authentication.

注意:
erberos 身份验证的处理是公开透明的。这里描述的委托方法不适用于 Kerberos 身份验证。

App Transport Security (ATS)
App 传输安全 (ATS)

Starting in iOS 9.0 and OS X v10.11, a new security feature called App Transport Security (ATS) is enabled by default for all HTTP connections made with NSURLSession. ATS requires that HTTP connections use HTTPS (RFC 2818).

For more information, see NSAppTransportSecurity in the Information Property List Key Reference.

从 iOS9.0 和 OS X v10.11 开始,一种称为App 传输安全(ATS)的安全特性在所有用作 HTTP 连接的 NSURLSession 中是默认启用的。ATS 要求 HTTP 连接使用 HTTPS(RFC 2818)。

想了解更多信息,请看
Information Property List Key Reference 中的 NSAppTransportSecurity

Using an URL Session
使用 URL Session
使用 NSURLSession 类发起一个请求:

  1. 创建一个 session 配置。若是用作后台的 session,这个配置必须包括一个 唯一标识(unique identifier)。 存储这个标识, 当你的 app 崩溃或是被终止或是被挂起时用它来重连 session

  2. 创建一个 session, 指定它的配置对象,或是代理。

  3. 在每个代表一个资源请求的 session 中创建 task 对象。这些 task 对象应该是
    NSURLSessionTaskNSURLSessionDataTask,NSURLSessionUploadTask, or NSURLSessionDownloadTask 的子类,具体用哪个取决于你想要做什么。

每个task 开始时都处于挂起状态。你的 App 调用恢复任务后,它会开始下载其对应的资源。

在你开始一个 task 之后,session 会调用其代理方法,如下:

  1. 如果和服务器初始化握手需要一个连接 level 的挑战(比如SSL 的客户端证书),NSURLSession 会调用 URLSession:task:didReceiveChallenge:completionHandler: 或是 URLSession:didReceiveChallenge:completionHandler: 代理方法,如前面的 身份验证和 TLS 定制 所述。
    了解更多为 NSURLSession 写一个授权代理方法的信息,请阅读 read URL Session Programming Guide.

  2. 如果这个 task 的数据是由流提供的,NSURLSession 对象将会调用代理的 URLSession:task:needNewBodyStream: 代理方法来获取一个 提供新请求的 body data 的 NSInputStream 对象。

  3. 在初始化上传到服务器(如果可用的话)的 body 内容期间,代理会阶段性接收 URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend: 的回调来报告上传进度;

  4. 服务器发送一个响应;

  5. 如果响应表明需要授权,session 会调用代理方法
    URLSession:didReceiveChallenge:completionHandler: ,回到步骤2;

  6. 如果响应是一个 HTTP 重定向响应, NSURLSession 对象会调用代理方法
    URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler: 代理方法会用提供的 NSRULRequest 对象(遵循重定向),或是新的 NSURLRequest 对象(如果需要重定向到一个不同的 URL 的话),或是 nil(将有效的响应作为重定向响应的 body,并将它作为结果返回) 来调用所提供的 completion handler 。

  • 如果你决定追踪重定向,返回步骤2;
  • 如果代理没有实现这个方法,这个重定向将会被重定向的最大数字跟进。
  1. 对于一个通过调用
    downloadTaskWithResumeData:
    downloadTaskWithResumeData:completionHandler: 方法创建的(重复)下载任务来说,NSURLSession 会对一个新的任务对象调用
    URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes: 代理方法。

  2. 对于数据任务,NSURLSession 会调用
    URLSession:dataTask:didReceiveResponse:completionHandler: 代理方法,来决定是否要将 数据任务转换成下载任务,然后调用完成回调来继续接收或下载数据。 如果你的 app 选择将数据任务转换成下载任务,NSURLSession 会将这个新的下载任务作为一个参数来调用 urlSession:dataTask:didBecomeDownloadTask: 代理方法。调用完之后,代理不会从数据任务接收到进一步回调,而是从下载任务那里来接收回调;

  3. 在服务器传输过程中,代理会周期性地接收到一个 task-level 的回调方法来报告传输的进度。 对于数据任务,session 会在接收过程中通过真正的数据块来调用
    URLSession:dataTask:didReceiveData: 代理方法。
    对于下载任务来说,session 会通过已经成功写入磁盘的 bytes 来调用
    urlSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite: 代理方法。如果用户告诉 app 来暂停下载,通过cancelByProducingResumeData: 然后,如果用户要求 app 继续下载任务,将返回的恢复数据给
    downloadTaskWithResumeData:
    downloadTaskWithResumeData:completionHandler: 方法来创建一个新的下载任务,来继续下载。(返回步骤1)

  4. 对于数据任务,NSURLSession 对象可能会调用
    URLSession:dataTask:willCacheResponse:completionHandler: 方法。你的 app 应该来决定是否允许缓存。如果你没有是现在合格方法,默认会使用 session 的配置对象中的缓存方案。

  5. 如果响应是由多个部分组成的编码,session 会在didiRecieveReponse代理被多次调用之后再次调用 didRecieveData 代理方法。如果出现这样的情况,回调步骤8(处理 didRecieveResponse回调)

  6. 如果下载任务成功完成, NSURLSession 对象会通过一个临时文件的地址来调用任务的 urlSessionDownloadTask:didFinishDownloadingToURL: 方法。你的 app 必须从这个文件中读取返回的数据,或是在这个代理方法 结束之前将它移到一个永久保存的地址。

  7. 任务完成时, NSURLSession 对象会通过一个错误对象或是 nil (如果任务成功完成)来调用 urlSession:task:didCompleteWithError:
    如果这个下载任务是可被重新唤起的,这个 NSError 对象的 userInfo 字典中会包含 key 为
    NSURLSessionDownloadTaskResumeData的值。你要将这个值传给
    downloadTaskWithResumeData:
    或是 downloadTaskWithResumeData:completionHandler: 来创建一个新的下载任务以继续这个已经存在的下载任务。
    如果这个下载任务不能被重新唤起,你的 app 需要创建一个新的下载任务来从头开始下载。
    不管是哪种情况,如果传输因任何服务器错误以外的原因而失败,返回到步骤3(创建或唤起任务对象)。

注意:
NSURLSession 不会通过 error 参数返回任何的服务器错误。通过 error 参数返回的错误只会是服务器端错误,比如不能解决 hostname,不能连接到 host。error codes 信息可在 URL Loading System Error Codes 中查询。
服务器端错误会通过 NSHTTPURLResponse 对象中的HTTP 状态码来返回,了解更多信息,请阅读 NSHTTPURLREsponseNSURLResponse的文档。

14 . 如果你不需要用到 session 了,可以通过调用 invalidateAndCancel(用来取消未完成的任务)或是 finishTasksAndInvalidate(在这个对象失效前完成未完成的任务)。
如果你不使这个session失效,它会在你的app终止时自动消失(除非它是一个含有正在进行的任务的后台 session) session 失效后,当所有未完成的任务被取消或是完成时,session会调用 urlSession:didBecomeInvalidWithError: 方法。当代理方法返回时,session 会处置对代理的强引用。

如果你的app 取消了一个正在进行的下载任务, 当出现错误时,NSURLSession 对象会调用代理的 URLSession:task:didCompleteWithError: 方法。

Background Transfer Considerations
后台处理注意事项
因为重启app(或是等待用户重新唤起时)代价是相当高的,在后台session中有些特性是不可用的。如下:

  • session 必须提供一个传递事件的代理,因为在传输进行中app可能会退出或是重启,完成事件的回调 Block 是不支持的、(为了上传和下载),这些代理在传输过程中表现是相同的;

  • 只有 HTTP 和 HTTPS 协议是被支持的。其他内置的网络协议和用户网络协议都不被支持;

  • 只有上传和下载是被支持的(没有 data 任务);
    重定向一直被允许;
    全系统同时进行的后台传输的数量是被限制的;

  • 如果后台任务未能满足指定系统吞吐量限制,可能会被取消。也就是说,如果一个长期运行的任务在一段时间内没有发送或者接受足够的数据,它可能会被取消,以后再被唤起。所以,如果可能的话,让一个传输可被重新唤起是很重要的。

  • 如果后台传输初始化时app是在后台,那这个任务将被当做可裁剪的。换言之,它将被当做 配置对象的 discretionary 属性为 true 的session中的一个任务。

如果这些限制和你 app 的需求有冲突,你可以在 non-background session 中将远程资源下载到一个文件中。这样,当你的用户让你的 iOS app 进入后台或是退出你的 OS X app 时,可以通过调用 cacelByProducingResumeData: 方法来暂停任何进行中的下载任务。当用户重新让 app 进入前台时恢复下载。如果你的 app 在你获取到任何恢复的数据之前终止了,就不能再恢复下载了。

注意:
后台 session 是为了优化传输少量很大的资源,在必要时可以进行续传。如果可以,你可能想要调查优化服务器端行为的方法 ,来实现这样的用法,比如:

  • 在终结点发起发送或接收 zip 或 tar格式的压缩文件的请求,而不是分开调用多次;
  • 在终结点发起发送或接收在服务器和客户端之间的增量差异的请求;
  • 在终结点发起一个可返回上传ID的请求,这个ID可用来追踪和恢复传输到服务器的数据;
  • 添加一个中间的web 服务器代理请求到规范的web服务器,以方便任何上述优化。

NSCopying Behavior
session 和 task 对象都遵从 NSCopying 协议,如下:

  • 当你的app 拷贝一个session 或是 task 对象时,你会得到一个同样的对象;
  • 当你的app拷贝一个配置对象时,你会得到一个你可以独立地修改的拷贝对象。

Thread Safety
线程安全
URL Session API 自身完全是线程安全的。你可以在任何一个线程上下文中随意创建 session 和 task,而且,当你的代理方法调用提供的 完成回调时,它的工作已自动被安排在正确的代理队列中。

警告:
你的 URLSessionDidFinishEventsForBackgroundURLSession: 代理方法可能会在第二线程中被调用。但是,在iOS中,你完成那个方法时需要在
application:handleEventsForBackgroundURLSession:CompletionHandler: app 代理方法中调用completion handler。而且你必须在主线程中调用那个 completion handler。


NSURLSession 有太多的东西值得我们去学习推敲,方方面面都透露着学习的必要性,学习总结,一劳永逸,对我们日后开发一定会受益良多。
作为一枚成长中的菜鸟,分享和开源精神,还是值得具备的。

期待


  • 如果在阅读过程中遇到 error || new ideas,希望你能 messages 我,我会及时改正谢谢。
  • 点击右上角的 喜欢 和 订阅Rss 按钮,可以收藏本仓库,并在 Demo 更新时收到邮件通知。
❄︎ 本文结束    感谢简阅 ^_^. ❄︎

本文标题:iOS NSURLSessionApple译文

文章作者:寄己的路

原始链接:https://sunyonghui.github.io/AppleTranslation/NSURLSessionApple.html

版权声明: 署名-非商业性使用-禁止演绎 4.0 国际 本博客所有文章除特别声明外均为原创,转载务必请「注明出处链接(可点击) - 作者」,并通过E-mail等方式告知,谢谢合作!