import Cocoa
class Operation : NSObject {
/// 如果operation执行的是异步任务, 需要重写此方法。
/// 可以直接调用start方法, 这将会在当前线程中执行operation任务; 也可以将operation加入到队列中, 由列队来创建线程, 执行start方法。
/// 重写此方法时, 禁止调用父类的start方法。
/// 重写此方法时, 需要手动管理各项属性, 并发送相应的KVO通知:
/// - 需要反复查看isCanceled, 以确保任务被取消时执行正确操作
/// - 需要在任务结束时设置isExecuting, 当任务被取消时也要更新isExecuting
/// - 需要在任务结束时设置isFinished, 当任务被取消时也要更新isFinished
/// - 如果我们自定义了dependency, 还需要更新isReady
/// - 需要设置isAsynchronous为true
func start()
/// 如果operation执行的是同步任务, 需要重写此方法。
/// 重写此方法时, 需要手动管理各项属性, 并发送相应的KVO通知:
/// - 需要反复查看isCanceled, 以确保任务被取消时执行正确操作
/// - 需要在任务结束时设置isExecuting, 当任务被取消时也要更新isExecuting
/// - 需要在任务结束时设置isFinished, 当任务被取消时也要更新isFinished
/// - 如果我们自定义了dependency, 还需要更新isReady
/// - 需要设置isAsynchronous为false
func main()
/// 只读属性,任务是否已被取消。
/// 这个属性会在执行cancel方法时, 自动被设置为true
/// 在operation任务的代码中, 需要检查这个值, 以确定是否立刻结束任务
var isCancelled: Bool
/// 仅仅标记isCancelled属性为true,并不会退出任务,和NSThread的cancel类似机制。
/// 需要在operation任务的代码中, 检查isCancelled, 以确定是否立刻结束任务
func cancel()
/// 只读属性,任务是否正在执行。
/// 需要重写属性, 以加入setter方法。
/// 需要手动调用KVO方法来进行通知。
/// 需要在operation任务的代码中,手动设置这个属性。
var isExecuting: Bool
/// 只读属性,任务是否结束。
/// 需要重写属性, 以加入setter方法。
/// 需要手动调用KVO方法来进行通知。
/// 当任务加入到列队中后, 队列会监听该属性的值。当isFinished设置为true后,队列将会移除这个任务。
/// 任务很可能是异步的,start方法返回也不代表任务就结束了。任务结束需要开发者手动修改该属性的值,队列就可以正常的移除任务。
var isFinished: Bool
/// 该属性已经被标识即将弃用,具体含义参考请isAsynchronous。
var isConcurrent: Bool
/// 只读属性,是否为异步任务,默认false。
/// 重写此属性, 将值更改为true。
var isAsynchronous: Bool
/// 只读属性,任务是否准备就绪
/// 需要重写属性, 以加入setter方法。
/// 需要手动调用KVO方法来进行通知。
/// 当任务加入到列队中后, 队列会监听该属性的值。当isReady设置为true后,该任务即将开始执行。
/// 如果存在依赖的任务没有执行完成isReady为false
var isReady: Bool
/// 添加依赖。如果一个任务有依赖,需要等待依赖的任务执行完成才能开始执行
func addDependency(_ op: Operation)
/// 删除依赖。
func removeDependency(_ op: Operation)
/// 在当前操作开始执行之前完成执行的所有操作对象数组。
var dependencies: [Operation]
/// 任务在队列里的优先级。
public enum QueuePriority : Int {
case veryLow
case low
case normal
case high
case veryHigh
}
/// 任务在队列里的优先级。
/// 同一个列队中的任务, 处于isReady状态的任务, 才能执行; 存在多个isReady状态的任务时, queuePriority高的开始执行。
var queuePriority: Operation.QueuePriority
/// 任务完成后的回调方法。
/// 当isFinished属性设置为true时, 会执行该回调。
var completionBlock: (() -> Void)?
func waitUntilFinished()
var threadPriority: Double
var qualityOfService: QualityOfService
var name: String?
}
class OperationQueue : NSObject {
/// 系统规定的可以设定的最大任务并发数。
class let defaultMaxConcurrentOperationCount: Int
/// 向队列中添加一个任务。
/// 任务加入到列队后, 会立刻开始执行。
func addOperation(_ op: Operation)
/// 向队列中添加一组任务。
/// 任务加入到列队后, 会立刻开始执行。
/// - Parameters:
/// - ops: 一组任务
/// - wait: 当wait是true时, 阻塞当前线程直到所有任务完成; 当wait是false时, 不会阻塞当前线程
func addOperations(_ ops: [Operation], waitUntilFinished wait: Bool)
/// 向队列中添加一个任务,任务以block的形式传入,使用更方便。
/// 任务加入到列队后, 会立刻开始执行。
func addOperation(_ block: @escaping () -> Void)
/// 获取队列中的所有任务
var operations: [Operation]
/// 获取队列中的任务数量
var operationCount: Int
/// 设置队列支持的最大任务并发数。
/// - count = -1, 默认值, 没有并发数限制, 所有任务并发执行。
/// - count = 1, 队列为串行队列。所有的任务, 按照加入的顺序依次执行
/// - count > 1, 队列为并发队列。所有任务并发执行,这个值不会超过defaultMaxConcurrentOperationCount。
var maxConcurrentOperationCount: Int
/// 队列是否挂起。
/// - 设置为true时, 代表暂停队列
/// - 设置为false时, 代表恢复队列
///
/// 这里的暂停和取消(包括操作的取消和队列的取消)并不代表可以将当前的操作立即取消,而是当当前的操作执行完毕之后不再执行新的操作。
/// 暂停和取消的区别就在于:暂停操作之后还可以恢复操作,继续向下执行;而取消操作之后,所有的操作就清空了,无法再接着执行剩下的操作。
var isSuspended: Bool
/// 队列的名称
var name: String?
var qualityOfService: QualityOfService
unowned(unsafe) var underlyingQueue: DispatchQueue?
/// 取消队列中的所有任务
/// 即所有任务都执行cancel方法,所有任务的isCancelled属性都置为true
func cancelAllOperations()
/// 阻塞当前线程直到所有任务完成
func waitUntilAllOperationsAreFinished()
/// 类属性,获取当前线程中正在执行的队列
class var current: OperationQueue?
/// 类属性,获取主队列。
/// 任务添加到主队列就会使用主线程执行,主队列的任务并发数为1,即串行队列。但是不包括使用addExecutionBlock:添加的额外任务,额外任务可能在其他线程执行。
class var main: OperationQueue
}
/// 场景1: 从指定URL中下载数据
class Crawler : Operation, URLSessionDownloadDelegate {
private var url : URL
private var session : URLSession!
private var task : URLSessionDownloadTask!
private var _isFinished : Bool = false
/// 需要重写这个属性, 才有setter方法
/// 这只是个计算属性, 为了getter方法, 需要另外一个值作为存储属性
/// 需要加入方法, 以触发KVO, 本例中发现不需要指定dynamic关键字
/// TODO: 目前不清楚isExecuting能触发什么操作
override var isFinished: Bool {
set {
willChangeValue(forKey: "isFinished")
_isFinished = newValue
didChangeValue(forKey: "isFinished")
}
get {
return _isFinished
}
}
init(_ url: String) {
self.url = URL(string: url)!
super.init()
self.session = URLSession(configuration: .default, delegate: self, delegateQueue: nil)
}
override func start() {
task = session.downloadTask(with: url)
task.resume()
}
override var isAsynchronous: Bool {
return true
}
override func cancel() {
super.cancel()
task.cancel()
}
/// 本例中调用了cancel方法, 代码也一定是在这个委托方法中结束, 并不会在后面两个方法中结束, 这说明operation.cancel方法并不能强制结束正在执行的任务
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
if let error = error as NSError? {
print("\(name!) didCompleteWithError: \(error.localizedDescription)")
} else {
print("didComplete")
}
isFinished = true
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
if isCancelled {
print("\(name!) didFinishDownloadingTo = \(location.absoluteString), cancelled")
} else {
print("\(name!) didFinishDownloadingTo = \(location.absoluteString)")
}
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
if isCancelled {
print("\(name!) totalBytesWritten = \(totalBytesExpectedToWrite) | \(totalBytesWritten), didWriteData = \(bytesWritten), cancelled")
} else {
print("\(name!) totalBytesWritten = \(totalBytesExpectedToWrite) | \(totalBytesWritten), didWriteData = \(bytesWritten)")
}
}
}
/// KVO 需要在对象中执行
class Example : NSObject {
func test() {
let op1 = Crawler("http://m6.pc6.com/xuh6/eZip173.dmg")
op1.name = "op1"
// Crawler中指定了 isFinished = true, 才会执行这个闭包
op1.completionBlock = {
print("op1 is completion")
}
op1.addObserver(self, forKeyPath: "isFinished", options: .new, context: nil)
let op2 = Crawler("http://m6.pc6.com/xuh6/winzip654149.dmg")
op2.name = "op2"
op2.completionBlock = {
print("op2 is completion")
}
op2.addObserver(self, forKeyPath: "isFinished", options: .new, context: nil)
let queue = OperationQueue()
// 指定为1时, op1和op2按照先后加入的顺序, 依次执行
//queue.maxConcurrentOperationCount = 1
queue.addOperation(op1)
queue.addOperation(op2)
DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + 3) {
queue.cancelAllOperations()
}
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
guard let op = object as? Crawler else {
print("object is not a `Crawler`")
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
return
}
if keyPath != "isFinished" {
print("keyPath is `\(keyPath!)`")
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
return
}
let newValue = change![.newKey] as! Bool
print("\(op.name!).isFinished change to \(newValue)")
}
}
let example = Example()
example.test()