大家在进行iOS开发的时候一定会用到网络操作。但由于早期原生的 NSURLConnection 操作起来有很多不便,使得大家更愿意使用第三方库的解决方案,比如鼎鼎大名的 AFNetworking。正是因为这点,苹果团队为开发者提供了改进后的原生网络库支持,也就是 NSURLSession。

NSURLSession 简介

NSURLSession 是苹果为我们提供的一套新的网络处理库。它的前身是 NSURLConnection,但由于 NSURLConnection 提供的接口使用起来不够简洁,让很多开发者不愿意使用。按照苹果官方的文档上说的 NSURLSession 的出现就是为了代替 NSURLConnection。NSURLSession 提供了一套更优秀的网络处理解决方案,并且使用的接口更加简单,对开发者更加友好。

那么,使用 NSURLSession 和第三方库相比有什么好处呢,这些优秀的第三方库也提供了相应的网络处理能力。

更新后的 NSURLSession 提供了更加简洁的接口,所以我们不用担心之前 NSURLConnection 使用复杂的问题,从基本的网络请求需求来看,已经和诸如 AFNetworking 这些第三方库没有太多差别了。并且,使用 NSURLSession 的一个最大的好处就是它系统原生提供的,所以我们不需要做任何的额外导入的操作,就可以直接使用。

如果你不需要使用 AFNetworking 这些第三方库所提供的加强功能,比如和 UIKit 的深度集成等,那么使用 NSURLSession 是一个最简单快捷的选择。

使用 NSURLSession 进行基本的网络请求也非常的容易:

if let url = NSURL(string: "https://httpbin.org/get") {

    NSURLSession.sharedSession().dataTaskWithURL(url){ data, response, error in

        //... 

    }.resume()

}

这里使用 NSURLSession.sharedSession() 来获取 NSURLSession 的实例,然后调用 dataTaskWithURL 方法传入我们要访问的 url,最后在闭包中处理请求的返回结果。

注意,resume() 方法的调用,NSURLSession 默认是不启动的,我们必须手工调用 resume() 方法,才会开始请求。

以上代码是 NSURLSession 进行网络请求最简单的调用形式。

NSURLSession 详细接口

我们刚刚看到了 NSURLSession 的最简调用形式,当然它还支持很多网络请求特性的处理,我们来进一步了解。

NSURLSession 本身是不会进行请求的,而是通过创建 task 的形式进行网络请求,同一个 NSURLSession 可以创建多个 task,并且这些 task 之间的 cache 和 cookie 是共享的。那么我们就来看看 NSURLSession 都能创建哪些 task 吧。

  • NSURLSessionDataTask: 这个就是我们第一个例子中创建的 DataTask,它主要用于读取服务端的简单数据,比如 JSON 数据。
  • NSURLSessionDownloadTask: 这个 task 的主要用途是进行文件下载,它针对大文件的网络请求做了更多的处理,比如下载进度,断点续传等等。
  • NSURLSessionUploadTask: 和下载任务对应,这个 task 主要是用于对服务端发送文件类型的数据使用的。

好了,所有的 task 类型都在这里了。我们可以看几个例子,比如如何下载文件:

let imageURL = NSURL(string: "https://httpbin.org/image/png")!

NSURLSession.sharedSession().downloadTaskWithURL(imageURL) { location, response, error in

    guard let url = location else { return }
guard let imageData = NSData(contentsOfURL: url) else { return }
guard let image = UIImage(data: imageData) else { return } dispatch_async(dispatch_get_main_queue()) { //... } }.resume()

下载文件的时候,我们使用 downloadTaskWithURL 方法,这个方法的闭包中会接受一个 location 参数,这个参数表示我们下载好的文件的存放位置。

注意,downloadTaskWithURL 会将文件保存在一个临时目录中,location 参数指向这个临时目录的位置,如果我们要将下载好的文件进行持久保存的话,我们还需要将文件从这个临时目录中移动出来。

我们通过 location 参数可以找到文件的位置,然后将文件的内容读取出来,就像我们上面的例子中那样。

我们再来看一下上传操作:

let uploadURL = NSURL(string: "https://httpbin.org/image/png")!
let request = NSURLRequest(URL: uploadURL) let fileURL = NSURL(fileURLWithPath: "pathToUpload")
NSURLSession.sharedSession().uploadTaskWithRequest(request, fromFile: fileURL) { data, response, error in }.resume()

上传操作使用 uploadTaskWithRequest 方法。

这样,NSURLSession 的三种 task 我们就都了解了,它们的关系大致如下:

NSURLSessionConfiguration

刚刚我们一起了解了 NSURLSession 可以创建的三种 task 类型,那么我们在回过头来看看 NSURLSession 本身。我们前面的所有例子中,都是用 NSURLSession.sharedSession() 这样的方式得到的 NSURLSession 的实例,这个实例是全局共享的,并且功能受限。比如,由于全局实例没有代理对象,我们就不能够检测诸如下载进度这类的事件。以及我们无法设置后台下载的机制,等等。

当然 NSURLSession 的空间还是很大的,我们不仅能通过 NSURLSession.sharedSession() 这种方式得到实例,还可以创建我们自己的 NSURLSession 实例。 NSURLSession 定义了两个构造方法:

init(configuration:)
init(configuration:delegate:delegateQueue:)

这两个方法都会接受一个 NSURLSessionConfiguration 对象,这个对象定义了这个 NSURLSession 实例的各种配置信息。并且, NSURLSessionConfiguration 提供了三个默认的初始化方法:

  • defaultSessionConfiguration - 这个配置会使用全局的缓存,cookie 等信息,这个相当于 NSURLSessionConfiguration 的默认配置行为。
  • ephemeralSessionConfiguration - 这个配置不会对缓存或 cookie 以及认证信息进行存储,相当于一个私有的 Session,如果你开发一个浏览器产品,这个配置就相当于浏览器的隐私模式。
  • backgroundSessionConfiguration - 这个配置可以让你的网络操作在你的应用切换到后台的时候还能继续工作。

现在我们了解了这几个配置模式,就可以根据我们需要的网络操作类型确认相应的配置模式,如果是进行一般的数据读取,那么就可以使用 defaultSessionConfiguration,如果要进行隐私模式浏览等操作,就可以使用 ephemeralSessionConfiguration。最后,如果你需要开发一个下载功能,为了保证下载线程最大的执行空间,那么就可以使用 backgroundSessionConfiguration。

现在我们来使用 backgroundSessionConfiguration 进行下载操作:

let imageURL = NSURL(string: "https://httpbin.org/image/png")!
var session = NSURLSession(configuration: NSURLSessionConfiguration.backgroundSessionConfigurationWithIdentifier("download"))
session.downloadTaskWithURL(imageURL).resume()

我们创建 backgroundSessionConfiguration 的时候,还传入了一个字符串 "download",这个用作当前下载任务的标识,用于保证下载任务在后台的运行。

除了这三种预设的模式之外 NSURLSessionConfiguration 还可以进行很多的配置。 timeoutIntervalForRequest 和 timeoutIntervalForResource 可以控制网络操作的超时时间。 allowsCellularAccess 属性可以控制是否允许使用无线网络。HTTPAdditionalHeaders 可以指定 HTTP 请求头。

NSURLSessionConfiguration 几乎可以完成网络操作的大多数配置功能,并且这些配置都绑定到当前的 Session 中,我们一旦用配置好的 NSURLSessionConfiguration 初始化 NSURLSession 实例后,就不能修改这个 NSURLSession 相关的配置了。所以,一切的配置操作都放在初始化 NSURLSession 之前。

使用代理

我们前面的例子都是通过一个闭包在网络操作完成的时候进行处理。那么有什么方法可以监听网络操作过程中发生的事件呢,比如我们下载一个大文件的时候,如果要等到下载完成可能会需要比较长的事件,这时候更好的体验是能够提供一个下载进度。类似这样的事件我们就需要用到代理。

我们在使用三种 task 的任意一种的时候都可以指定相应的代理。NSURLSession 的代理对象结构如下:

  • NSURLSessionDelegate - 作为所有代理的基类,定义了网络请求最基础的代理方法。

  • NSURLSessionTaskDelegate - 定义了网络请求任务相关的代理方法。

  • NSURLSessionDownloadDelegate - 用于下载任务相关的代理方法,比如下载进度等等。

  • NSURLSessionDataDelegate - 用于普通数据任务和上传任务。

我们可以用代理来检测下载进度:

class Downloader:NSObject, NSURLSessionDownloadDelegate {

    var session: NSURLSession?

    override init() {

        super.init()

        let imageURL = NSURL(string: "https://httpbin.org/image/png")!
session = NSURLSession(configuration: NSURLSessionConfiguration.backgroundSessionConfigurationWithIdentifier("taask"), delegate: self, delegateQueue: nil)
session?.downloadTaskWithURL(imageURL).resume() } func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didFinishDownloadingToURL location: NSURL) {
print("下载完成")
} func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
print("正在下载 \(totalBytesWritten)/\(totalBytesExpectedToWrite)")
} func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didResumeAtOffset fileOffset: Int64, expectedTotalBytes: Int64) { print("从 \(fileOffset) 处恢复下载,一共 \(expectedTotalBytes)") } }

我们的 Downloader 类实现了 NSURLSessionDownloadDelegate 协议,并实现了这个协议的三个方法,分别用于接收下载完成的通知,下载进度变化的通知,以及下载进度恢复的通知。

注意,Downloader 同时也继承自 NSObject,这个是必须的,否则我们在实现 NSURLSessionDownloadDelegate 协议的方法时会报错。这个只有在 Swift 中需要显示的继承,在 Objective-C 中则不需要,因为 Objective-C 中的任何类都是继承自 NSObject 的。

结语

NSURLSession 提供了网络请求相关的大部分方法,已经可以满足我们日常的需求,并且提供了很简单的调用接口。它是系统提供的库,所以我们不需要进行任何的库引用,就可以使用 NSURLSession 了。

NSURLSession 除了我们介绍的支持 task 特性,NSURLSessionConfiguration 配置对象,以及代理之外还提供了很多关于网络请求的相关特性,比如缓存控制,Cookie 控制,HTTP 验证操作等等。总之 NSURLSession 简单的接口之外,也提供了强大的体系。

NSURLSession 相比 Alamofire 这些第三方库来说也有一些不足,比如它没有提供很方便的自动数据类型转换。比如,Alamofire 中可以自动将服务端返回的 JSON 数据识别并解析出来,而使用 NSURLSession 则需要自己来完成。

至于使用 NSURLSession 还是 Alamofire 就要看各位自己的权衡了,如果没有特别依赖于第三方库提供的附加功能,我个人更加倾向于使用 NSURLSession。毕竟它不需要导入任何外部资源。

关于 NSURLSession 的基本讨论我们就完成了,大家还可以参考这些相关内容:

您还可以在 Github 上面下载文章中的示例代码: https://github.com/swiftcafex/NSURLSessionSamples

NSURLSession 网络库 - 原生系统送给我们的礼物的更多相关文章

  1. Unity跨平台C/CPP动态库编译---可靠UDP网络库kcp基于CMake的各平台构建实践

    1.为什么需要动态库 a)提供原生代码(native code)的支持,也叫原生插件,但是我实践的是c/cpp跨平台动态库,这里不具体涉及安卓平台java库和ios平台的objectc库构建. b)某 ...

  2. handy网络库源码阅读

    简洁易用的C++11网络库,From:https://github.com/yedf/handy 在整理过去的资料过程中,发现过去有关注过这一个网络库,简单看了一下属于轻量级的实现,因此本文将对该库进 ...

  3. Gevent高并发网络库精解

    进程 线程 协程 异步 并发编程(不是并行)目前有四种方式:多进程.多线程.协程和异步. 多进程编程在python中有类似C的os.fork,更高层封装的有multiprocessing标准库 多线程 ...

  4. 长文梳理muduo网络库核心代码、剖析优秀编程细节

    前言 muduo库是陈硕个人开发的tcp网络编程库,支持Reactor模型,推荐大家阅读陈硕写的<Linux多线程服务端编程:使用muduo C++网络库>.本人前段时间出于个人学习.找工 ...

  5. 《Linux多线程服务端编程:使用muduo C++网络库》上市半年重印两次,总印数达到了9000册

    <Linux多线程服务端编程:使用muduo C++网络库>这本书自今年一月上市以来,半年之内已经重印两次(加上首印,一共是三次印刷),总印数达到了9000册,这在技术书里已经算是相当不错 ...

  6. 跨平台高效率Lua网络库 ( 同步形式的API ,底层是异步非阻塞)

    Joynet 项目地址:https://github.com/IronsDu/Joynet 介绍 high performance network library for lua, based on  ...

  7. poco网络库分析,教你如何学习使用开源库

    Poco::Net库中有 FTPClient HTML HTTP HTTPClient HTTPServer ICMP Logging Mail Messages NetCore NTP OAuth ...

  8. boost.ASIO-可能是下一代C++标准的网络库

    曾几何时,Boost中有一个Socket库,但后来没有了下文,C++社区一直在翘首盼望一个标准网络库的出现,网络上开源的网络库也有不少,例如Apache Portable Runtime就是比较著名的 ...

  9. 【转】开源C/C++网络库比较

    在开源的C/C++网络库中, 常用的就那么几个, 在业界知名度最高的, 应该是ACE了, 不过是个重量级的大家伙, 轻量级的有libevent, libev, 还有 Boost的ASIO. ACE是一 ...

随机推荐

  1. [CSS] Reduce Ambiguity in Class Names using a Naming Convention

    A solid naming convention makes it less likely to run into naming conflicts and helps establish a se ...

  2. ConcurrentLinkedQueue的实现原理分析

    1.    引言 在并发编程中我们有时候需要使用线程安全的队列.如果我们要实现一个线程安全的队列有两种实现方式一种是使用阻塞算法,另一种是使用非阻塞算法.使用阻塞算法的队列可以用一个锁(入队和出队用同 ...

  3. CompletionService 和ExecutorService的区别和用法

    JavaSE5的Java.util.concurrent包中的执行器(Executor)将为你管理Thread对象,从而简化了并发编程.Executor在客户端和执行任务之间提供了一个间接层,Exec ...

  4. Android中的动画具体解释系列【3】——自己定义动画研究

    在上一篇中我们使用到了位移动画TranslateAnimation,以下我们先来看看TranslateAnimation是怎样实现Animation中的抽象方法的: /* * Copyright (C ...

  5. 浏览器对象模型bom的作用是什么?

    浏览器对象模型bom的作用是什么? 零.总结 1.BOM提供了独立于内容而与浏览器窗口进行交互的对象 2.BOM提供了一些访问窗口对象的一些方法,我们可以用它来移动窗口位置,改变窗口大小,打开新窗口和 ...

  6. php超实用正则表达式有哪些

    php超实用正则表达式有哪些 一.总结 一句话总结: 二.php几个超实用正则表达式 对于开发人员来说,正则表达式是一个非常有用的功能,它提供了 查找,匹配,替换 句子,单词,或者其他格式的字符串.这 ...

  7. SEO那些事:一句代码一键分享网站

    这是很久以前就已经写过的笔记了,有一个习惯,每次遇到一个问题,都会进行百度,然后把解决问题的关键点记录下来,有人问我,为什么更新频率如此之快,大部分都是从前积累的知识点. 其实每天工作所涉及的知识点都 ...

  8. C++ 快速入门笔记:进阶编程

    C++入门笔记:高级编程 文件和流 打开文件 void open (const char *filename, ios::openmode mode); ios::app 追加模式.所有写入都追加到文 ...

  9. erlang局域网内节点通信——艰难四步曲

    http://blog.chinaunix.net/uid-22566367-id-382011.html 在Programming Erlang这本书中,在写到第十章中,主要实现的是不同节点之间的通 ...

  10. 学习鸟哥的Linux私房菜笔记(3)——基础使用

    一.设备文件 设备在Linux中以特殊文件的形式存在 块(block)设备文件 字符(character)设备文件 设备文件所在位置 查看设备类型 二.虚拟控制台及用户身份切换 在系统中有12个虚拟控 ...