Swift 中的 Operation 和 OperationQueue 如何使用

Operation 在 Swift 中是一种非常好用的类,它可以根据职责将业务拆分成多个类,保持代码的易读性。同时它还可以跟踪任务执行进度和控制任务依赖关系。

它和 ObjC 中 NSOperation 是一样的,一般结合 OperationQueue 来使用。

Operation 详解

在日常开发中,一个 Operation 的实例通常负责单个同步任务。需要注意的是:Operation 是一个抽象类,我们不应该直接使用它。而是应该使用系统实现的子类 BlockOperation 或者自己集成它,来自定义实现一个子类。

Operation 的任务启动方式有两种:

  • 将其添加到 OperationQueue
  • 手动调用其 start 方法启动

推荐 由 OperationQueue 来管理状态。

使用系统定义的 BlockOperation 代码示例如下:

scss 复制代码
let blockOperation = BlockOperation { 
    print("执行任务")
} 
let queue = OperationQueue() queue.addOperation(blockOperation)

当然,也可以直接调用 OperationQueue 的方法来添加任务:

arduino 复制代码
queue.addOperation { 
    print("执行任务")
}

给定的任务被添加到 OperationQueue中,它将尽快开始执行。

如何自定义 Operation 的子类

你可以通过自定义实现其子类,来进行业务代码的解耦。比如你有一个需求:导入用户的数据并将其上传到服务器。你可以实现一个负责导入的 Operation、再创建一个负责上传的 Operation。

下面的代码示例展示了一个用于导入内容的自定义子类:

swift 复制代码
final class ContentImportOperation: Operation {

    let itemProvider: NSItemProvider

    init(itemProvider: NSItemProvider) {
        self.itemProvider = itemProvider
        super.init()
    }

    override func main() {
        guard !isCancelled else { return }
        print("数据正在导入")
    }根据操作当前的执行状态,操作可以处于几种状态
}

该类接受一个 itemProvider 的参数来接受导入的内容,并在 main 方法中将其导入。main 方法是唯一需要覆盖同步操作的方法。

将操作添加到队列中,并设置一个 block 来跟踪完成:

ini 复制代码
let fileURL = URL(fileURLWithPath: "xxxx")
let contentImportOperation = ContentImportOperation(itemProvider: NSItemProvider(contentsOf: fileURL)!)

contentImportOperation.completionBlock = {
    print("导入完成")
}

queue.addOperation(contentImportOperation)

通过自定义 ContentImportOperation,我们可以将导入内容相关的逻辑都放在其中,在该类中跟踪任务进度和完成情况,这样代码逻辑会很清晰,并且测试代码也非常好写。

Operation 的几种状态

根据 Operation 当前的执行状态,其可以处于几种状态:

  • Ready:准备好开始任务
  • Executing:任务正在执行中
  • Finished:任务已经完成
  • Canceled:任务已被取消

需要注意的是,一个 Operation 只能执行一次。无论何时它处于完成或取消状态,你都不能再重新启动同一个实例。

在自定义实现中,你需要在执行之前手动检查取消状态,以确保任务被取消。还有就是,当 Operation 在同一时间开始和取消时,可能会发生数据竞争的问题。

一旦任务完成,OperationQueue 就会自动从队列中删除任务,不仅仅只有执行完成才会删除任务,取消任务也会触发。

管理依赖

使用 Operation 的一个很大的好处是使用依赖关系。你可以轻松地在两个实例之间添加依赖项。例如:导入内容后开始上传:

ini 复制代码
let fileURL = URL(fileURLWithPath: "xxx")
let contentImportOperation = ContentImportOperation(itemProvider: NSItemProvider(contentsOf: fileURL)!)
contentImportOperation.completionBlock = {
    print("导入完成")
}

let contentUploadOperation = UploadContentOperation()
contentUploadOperation.addDependency(contentImportOperation)
contentUploadOperation.completionBlock = {
    print("上传完成")
}

queue.addOperations([contentImportOperation, contentUploadOperation], waitUntilFinished: true)

只有在内容导入完成后,上传任务才会开始执行。需要说一下的是:如果导入操作取消,上传仍将开始。因为对于 Operation 来说它并不关心任务是执行导致完成还是取消导致的完成。

所以如果你想取消导入并不触发上传的话,你还需要在上传中添加判断逻辑:

swift 复制代码
final class UploadContentOperation: Operation {
    override func main() {
        guard !dependencies.contains(where: { $0.isCancelled }), !isCancelled else {
            return
        }

        print("上传中")
    }
}
相关推荐
黑码哥几秒前
ViewHolder设计模式深度剖析:iOS开发者掌握Android列表性能优化的实战指南
android·ios·性能优化·跨平台开发·viewholder
2501_915106322 小时前
app 上架过程,安装包准备、证书与描述文件管理、安装测试、上传
android·ios·小程序·https·uni-app·iphone·webview
2501_915106322 小时前
使用 Sniffmaster TCP 抓包和 Wireshark 网络分析
网络协议·tcp/ip·ios·小程序·uni-app·wireshark·iphone
熊猫钓鱼>_>2 小时前
移动端开发技术选型报告:三足鼎立时代的开发者指南(2026年2月)
android·人工智能·ios·app·鸿蒙·cpu·移动端
徐同保1 天前
通过ip访问nginx的服务时,被第一个server重定向了,通过设置default_server解决这个问题
ios·iphone
2501_915918411 天前
在 iOS 环境下查看 App 详细信息与文件目录
android·ios·小程序·https·uni-app·iphone·webview
2501_916007471 天前
没有 Mac 用户如何上架 App Store,IPA生成、证书与描述文件管理、跨平台上传
android·macos·ios·小程序·uni-app·iphone·webview
夏幻灵2 天前
HTTPS全面解析:原理、加密机制与证书体
ios·iphone
TheNextByte12 天前
如何在iPhone上恢复已删除的笔记的综合指南
笔记·ios·iphone
rose and war2 天前
python和jinja版本问题导致的访问报500
python·ios