NS for iOS Devs —并发
在Swift Post上以更好的格式阅读此文章。
在专注于iOS中的Application Lifecycle和View Lifecyle之后,我们的下一个主题是concurrency 。 在当今世界,我们面临着同时处理多个操作的挑战。 我们希望缩短用户的等待时间,即使这并非总是必要的。 长时间显示负载或冻结UI会转移用户的注意力,并会不时减少应用程序的使用。 同时,保持注意力在应用程序中变得非常困难。 因此,我们不必并行运行操作,而必须并行运行它们,以获得更好的用户体验。
为什么我们需要非常了解并发性?
如今,几乎每个应用程序都可以进行在线数据传输(网络请求和响应)。 正确处理网络操作非常重要。 除了应用程序功能之外,我们不想在使用应用程序时打扰或阻止用户。 中断总是会导致不良的用户体验。 因此,当我们显示任何类型的UI时,我们通常同时处理网络请求。 我们可以在显示加载动画的同时发送网络请求并等待响应。 并发由iOS中的不同API处理。 我们将重点介绍基本的Grand Central Dispatch(GCD),以了解并发性。
在编程世界中,并发是一个大话题,并且具有至关重要的地位。 如果您想了解有关并发的更多信息,有关Quora的答案很好解释。
在谈论DispatchQueue
之前,我们应该首先了解队列。 因为DispatchQueue
可以在不同的队列上运行操作。
我们到底需要了解什么队列?
iOS中有两种类型的队列,即主队列和其他队列。 我们只有一个主队列,但是我们可以有多个后台队列,它们可以在其上运行不同的操作。 后台队列根据其优先级在线程池上运行。 但是主队列在主线程上运行。 所有UI(用户界面)更新都必须在主线程上运行。 因此,我们将主队列用于UI。
最重要的是这里只有一个主队列 。 主队列应合理使用。 由于每个UI更新都必须在主队列中完成,因此,如果我们对其他每个操作都使用主队列,则可以冻结UI操作。 例如,如果我们在主队列上发送网络请求并根据响应更新用户界面,我们将看到用户界面直到返回数据才响应。 提示是我们应该尽可能多地使用后台队列,并尝试将主队列留空用于非UI操作。
我们如何使用带有简单网络请求和GCD的队列?
发送网络请求非常简单。 URLSession
是用于简单网络请求的最常用方法。 我们可以通过dataTask
发送请求,并在闭包中获取带有错误(如果有)的响应数据。
最后,我们可以看到具有更好用户体验处理的示例代码。 下面的代码从后台队列开始,当需要在屏幕上显示加载指示器时,我们切换到主队列。 执行继续并启动任务。 收到响应后,我们仍在后台队列中。 如果出现错误,现在我们将切换到主队列并显示警报。
主线程与主队列以及它们为什么不相同可能会引起混淆。 有一个主线程和一个主队列。 如果您对更多细节感到好奇,可以阅读这篇文章。
我们已经讨论了一个简单的网络请求和GCD。 对于许多应用程序而言,同时处理更复杂的任务很重要,而GCD仅提供一种简单而直接的解决方案。 我们可能需要取消并发操作或遵循其状态。 GCD没有提供解决这些问题的方法。 这就是为什么我们将从Operation
和OperationQueue
获得帮助。 Operation
由OperationQueue
链接,它们也可以相互依赖。 例如,如果我们要在获取餐厅食物清单后下载食物图像,则应将获取操作添加为下载操作的依赖项,并将其放入同一OperationQueue
。 因此,下载操作将等待提取操作完成。
让我们看一下基本的Operation
示例:
与其讨论过多有关Operation
和OperationQueue
细节,不如让我们看一下我们应该知道的重要事项:
- 操作对象是单发对象 。 这是不可重用的。 当我们使用操作对象时,基本上就完成了。 如果要重复相同的操作,则需要从自定义操作类中创建另一个实例。
-
Operation
是与单个任务相关联的抽象类。 因此,我们无法创建Operation
新实例。 相反,我们可以像示例中那样将其子类化,也可以使用系统范围内定义的子类(例如BlockOperation
)。 如果操作具有依赖项,则在所有依赖项完成执行之前,它不会被视为就绪。 当最后一个依赖项完成时,该操作开始执行。 -
Operation
符合KVC和KVO(我们将在后面讨论)。 因此,我们可以通过附加观察者来观察操作的变化。 但是我们不应该将操作绑定到UI元素。 因为UI元素必须在主线程上执行。 - 从多个线程调用
Operation
的方法是安全的,但我们应注意重写方法和自定义实现中的线程安全性。 - 如果我们要创建一个非并发操作,则仅重写
main()
方法就足够了。 但是对于并发操作,我们需要至少覆盖start()
,isAsynchronous
,isExecuting
和isFinished
。 在示例中,我们有一个并发操作。 这就是为什么我们用数字2
注释所有覆盖的方法和属性的原因。 但是我们没有重写start()
方法。 原因是start()
main()
方法在被调用时会自动调用main()
方法。 覆盖isExecuting
和isFinished
方法时,我们必须在操作完成或取消时生成KVO通知。 在示例中,我们可以看到这些部分以及带有数字1
的注释。 - 如果我们重写
start()
方法,则永远不要在自定义实现中调用super
。
如果我们想保持应用程序的响应速度并轻松地并行运行多个进程,则必须了解并发性并根据需要使用我们的工具。 GCD使我们变得简单。 但是,当我们想要进行高级和自定义操作(例如取消执行或将代码的某些部分重用于并发操作)时,这仅仅是不够的。 在这些情况下,使用Operation
和OperationQueue
会减轻痛苦。 GCD和Operation是不同的东西。 两者的便捷使用对于创建健壮且用户友好的iOS应用至关重要。
在处理并发流程时,您遵循哪些策略? 您如何看待GCD的简单解决方案,以及如何使用它? 您是否曾经遇到过一个复杂的问题,您可以在Operation
的帮助下迅速地解决问题? 让我知道您对Twitter @candostEN或下面的评论的想法,评论或反馈。
NS for iOS Devs Series的所有帖子
- 应用生命周期
- 查看生命周期
- 并发
- 可测性
进一步:
Apple派遣文件
苹果操作文档
了解有关并发,异步与同步的更多信息
在大中央调度区深潜
NSHipster — NSOperation
Swift中的Operation和OperationQueue教程
了解Swift中的操作和操作队列