# Operation的使用

> 当前环境： Xcode10.0 Swift4.2 iOS SDK 12.1

Demo 下载： <https://github.com/zColdWater/OperationDemo>

## 前言

> 之前的工作中一直没有怎么接触 Operation 来管理多线程，直到最近在做项目的时候需要使用多线程队列等复杂的任务管理，我开始逐步接触 Operation 和使用它带给我的方便。

在使用过程中我也在比对，什么时候应该用 GCD 合适 什么时候用 Operation 合适。 因为之前对 GCD 使用比较多一些所以有些问题能用 GCD 就用 GCD 完全没有考虑 Operation，直到最近使用 Operation 带来效益才觉得，有些情况更适合使用 Operation 来处理。

## 异步 Operation

```swift
/// 如果是需要异步执行代码
/// 需要覆盖: 1.isAsynchronous(默认是false,需要修改成true. 仅是给外界声明自己是 同步操作还是异步操作，没有什么特殊功能。)
/// 需要覆盖: 2.isExecuting 当开始任务 设置成 Ture，执行完成后 设置成 False
/// 需要覆盖: 3.isFinished 当开始任务 设置成 False，执行完成后 设置成 True
/// 需要覆盖: 4.start 异步代码 编写的位置
/// 注意⚠️:  需要手动的来控制Operation状态，Operation才能结束。
class AsyncOperation: Operation {

    override init() {}

    var result: String = ""

    var taskName: String = ""

    override var isAsynchronous: Bool { return true }

    private let stateLock = NSLock()

    // 一定要写KVO 否则无法更改状态
    // willChangeValue && didChangeValue
    private var _executing: Bool = false
    override private(set) var isExecuting: Bool {
        get {
            return stateLock.withCriticalScope { _executing }
        }
        set {
            willChangeValue(forKey: "isExecuting")
            stateLock.withCriticalScope {
                if _executing != newValue {
                    _executing = newValue
                }
            }
            didChangeValue(forKey: "isExecuting")
        }
    }

    // 一定要写KVO 否则无法更改状态
    // willChangeValue && didChangeValue
    private var _finished: Bool = false
    override private(set) var isFinished: Bool {
        get {
            return stateLock.withCriticalScope { _finished }
        }
        set {
            willChangeValue(forKey: "isFinished")
            stateLock.withCriticalScope {
                if _finished != newValue {
                    _finished = newValue
                }
            }
            didChangeValue(forKey: "isFinished")
        }
    }

    override func start() {
        launchOperation()
        let queue: DispatchQueue = DispatchQueue(label: "serialQueue")
        queue.async {
            // 当前线程睡10s
            sleep(10)

            // 结果赋值
            if self.taskName == "任务1" {
                self.result = "I"
            }
            if self.taskName == "任务2" {
                self.result = "Love"
            }
            if self.taskName == "任务3" {
                self.result = "U"
            }

            // 改变Operation状态告诉外界，我执行完了，结束了。
            self.completeOperation()
        }
    }

    func completeOperation() {
        isExecuting = false
        isFinished = true
    }

    func launchOperation() {
        isExecuting = true
        isFinished = false
    }
}

extension NSLock {
    func withCriticalScope<T>(block: () -> T) -> T {
        lock()
        let value = block()
        unlock()
        return value
    }
}
```

## 同步 Operation

```swift
/// 如果是需要执行同步代码，只需要重写 main() 方法即可，
/// main() 填写同步的方法
/// 注意⚠️:  不需要手动控制自己的状态。
class SyncOperation: Operation {

    var result: String = ""
    var taskName: String = ""

    override func main() {

        if isCancelled { return }
        // 同步睡10s
        sleep(10)

        // 结果赋值
        if self.taskName == "任务1" {
            self.result = "I"
        }
        if self.taskName == "任务2" {
            self.result = "Love"
        }
        if self.taskName == "任务3" {
            self.result = "U"
        }

    }

}
```

## 需求实现

通过对 `同步操作` 和 `异步操作` 的封装得到：

1. 【任务 1】输出 'I' \[异步 Operation]
2. 【任务 2】输出 'Love'  \[同步 Operation]
3. 【任务 3】输出 'U'  \[异步 Operation]

我们期望输出是 'I Love U'，我们可以让三个任务并行去执行。

Demo Code

```swift
import UIKit

class ViewController: UIViewController,UITextFieldDelegate {


    @IBOutlet weak var input: UITextField!

    override func viewDidLoad() {
        super.viewDidLoad()
        input.delegate = self
        input.text = "1"
    }


    @IBAction func onLaunch(_ sender: Any) {

        let operation1: AsyncOperation = AsyncOperation()
        operation1.taskName = "任务1"
        let operation2: SyncOperation = SyncOperation()
        operation2.taskName = "任务2"
        let operation3: AsyncOperation = AsyncOperation()
        operation3.taskName = "任务3"

        let operation4 = BlockOperation {

            // 如果开启并发大于等于3，最早10s被打印出来
            // 如果并发设置成12，最早也要30s被打印出来
            print(operation1.result + " " + operation2.result + " " + operation3.result + "!!!")
        }

        operation4.addDependency(operation1)
        operation4.addDependency(operation2)
        operation4.addDependency(operation3)

        let queue = OperationQueue.init()
        // 这里设置成10 是让他们并发去执行。
        queue.maxConcurrentOperationCount = Int(input.text!) ?? 1
        queue.addOperations([operation1,operation2,operation3,operation4], waitUntilFinished: false)
        print("我肯定最早被打印出来!!!")
    }



    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {

        let aSet = NSCharacterSet(charactersIn:"0123456789").inverted
        let compSepByCharInSet = string.components(separatedBy: aSet)
        let numberFiltered = compSepByCharInSet.joined(separator: "")
        return string == numberFiltered
    }
}
```

## 补充

1. `OperationQueue` 有 `waitUntilAllOperationsAreFinished` 这个方法的，这个方法可以阻塞当前线程直到所有任务标记为完成，等同于 `queue.addOperations([operation1,operation2,operation3,operation4], waitUntilFinished: true)`


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://yongpenglovemimi123.gitbook.io/henry/ios/operation-de-shi-yong.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
