将iOS和Mac异步和相关任务封装到Cocoa Operation子类中

异步任务(例如,从网络中获取数据,解析,处理数据并将数据保存到本地缓存中)是当今应用程序执行的常规任务。 作为开发人员,我们必须确保UI /主线程运行平稳,并将长时间运行的繁重工作任务移入后台线程,以维持60 FPS动画。

Apple为开发人员提供了两种在后台线程中执行任务的方式:

  1. 大中央调度(GCD):开发人员可以使用队列在后台线程池中串行或并发执行任务的API集。
  2. 操作(也称为NSOperation):可可抽象类,代表要执行的单个任务单元。 它是一个线程安全类,具有开箱即用的内置状态,优先级和QoS,取消和依赖项管理。

在本文中,我们将构建一个异步的Operation子类,该子类可从GitHub API异步获取存储库,以及一个从属的Operation子类,可将获取的存储库数据解析并将其序列化为Swift类。

我们将建立什么

  • AsynchronousOperation:支持异步操作的Operation子类。
  • FetchRepoOperation:AsynchronousOperation子类,该类从上周开始使用URLSession异步获取最新趋势的GitHub存储库的数据。
  • ParseRepoDataOperation:操作子类,该子类使用Swift Codable和JSONDecoder将FetchRepoOperation中的数据解码并序列化为GithubRepo对象的数组。
  • 游乐场页面:使用OperationQueue执行操作,在操作之间添加依赖关系,并使用完成块在操作对象之间传递数据。

使用Operation子类实现异步操作

默认情况下,Operation Class同步运行代码。 Apple提供了一种通过子类化isAsynchronous布尔属性并将其重写为true来异步运行代码的方法。

我们还需要使用枚举添加我们自己的状态管理属性,处理从就绪,执行和完成状态的更改。 当同时读写状态属性时,dispacth队列将用于对状态属性使用调度屏障来处理同步。

在启动函数中,我们检查任务是否未取消,如果取消,则仅调用finish将状态更改为完成并返回。 如果没有,我们将状态设置为执行并调用主函数。 我们的子类将覆盖main函数,以执行函数内部的任务。

实现FetchRepoOperation来获取Github API

FetchRepoOperation是异步操作的子类,我们声明两个可选属性,fetchedData是一个Data对象,将用于存储来自API调用的数据响应,以及一个error属性,将用于存储来自API的错误如果发生,请致电。

在超类的重写的main方法中,我们构造URL和查询项,这些查询项将查询自上周以来创建的存储库,该存储库按星数降序排列。 之后,我们初始化URLRequest并使用URLSession调用异步数据任务。

在数据任务完成处理程序内部,我们将响应数据和错误分配给实例属性,然后调用finish方法将操作的状态设置为finish,以将操作标记为完成。

实施ParseRepoOperation以使用Swift Codable类解码JSON数据

我们创建GithubRepoFetchResult,GithubRepo,GithubOwner Swift类,该类实现了codable和CodingKeys枚举,以将json属性名称映射到实例属性骆驼案例名称。 通过使用Codable,我们可以利用JSONDecoder将Data解码为自动实现Codable的类。

实现ParseRepoOperation非常简单,我们将Operation用作子类,因为JSONDecoder的解码功能是同步的,因此我们不需要使用AsynchronousOperation。

我们声明了3个可选的实例属性,fetchedData是从FetchRepoOperation传递的数据,如果将数据解码为对象时发生错误,则错误为Error对象,包含GitHubRepo的repos数组将用于将JSONDecoding的结果存储到对象中。

在main函数内部,我们使用guard来解包可选的fetchedData,如果为nil,我们只是从函数中返回。 之后,在try catch块中,我们使用JSONDecoder解码函数,将fetchedData和GithubRepoFetchResult作为要解码的根类。 然后,我们将GithubRepoFetchResult中的items属性分配给repos实例属性。 如果解码时发生错误,我们会将错误分配给我们的错误实例属性。

使用OperationQueue执行操作

为了执行操作,我们使用OperationQueue,它充当优先队列,该队列使用先进先出机制来处理操作的执行。 我们将maxConcurrentOperationCount设置为1,因此我们的操作不会同时执行。

我们实例化FetchRepoOperation和ParseRepoOperation对象,然后将FetchRepoOperation对象添加为ParseRepoOperation对象的依赖项,因此将首先启动fetch任务,并且必须完成分析任务才能开始。

在操作之间传递数据并不容易,有许多方法可以做到,例如使用包含数据的数据包装器引用类,然后将其传递给每个操作。 对于此实现,我们将使用操作完成块,该操作将在操作完成时调用。 我们参考解析和提取操作对象为提取操作完成块分配一个闭包。 使用Unowned来避免保留周期,在该块内部,我们将获取响应数据传递给了解析的fetchedData属性。

我们为解析操作完成块属性分配一个闭包,该闭包仅循环存储库并将打印库的名称打印到控制台,以便我们看到结果。

最后,要开始操作,我们将调用包含获取和解析操作的数组传递给OperationQueue的addOperations来开始任务。

结论

可可操作类为开发人员提供了极大的灵活性,例如任务之间的依赖性,调整队列优先级和QoS,执行后台任务时的取消和状态管理。 就像Grand Central Dispatch(GCD)一样,它是用作iOS开发人员的好工具。 有效地使用它们将提供柔滑,高性能的iOS / Mac应用程序的结果。