Skip to content

Instantly share code, notes, and snippets.

@jeffery812
Forked from calebd/AsynchronousOperation.swift
Created February 14, 2018 13:58
Show Gist options
  • Save jeffery812/ca19daa7e344c298d1680fb760760e25 to your computer and use it in GitHub Desktop.
Save jeffery812/ca19daa7e344c298d1680fb760760e25 to your computer and use it in GitHub Desktop.
Concurrent NSOperation in Swift
import Foundation
/// An abstract class that makes building simple asynchronous operations easy.
/// Subclasses must implement `execute()` to perform any work and call
/// `finish()` when they are done. All `NSOperation` work will be handled
/// automatically.
open class AsynchronousOperation: Operation {
// MARK: - Properties
private let stateQueue = DispatchQueue(
label: "com.calebd.operation.state",
attributes: .concurrent)
private var rawState = OperationState.ready
@objc private dynamic var state: OperationState {
get {
return stateQueue.sync(execute: { rawState })
}
set {
willChangeValue(forKey: "state")
stateQueue.sync(
flags: .barrier,
execute: { rawState = newValue })
didChangeValue(forKey: "state")
}
}
public final override var isReady: Bool {
return state == .ready && super.isReady
}
public final override var isExecuting: Bool {
return state == .executing
}
public final override var isFinished: Bool {
return state == .finished
}
public final override var isAsynchronous: Bool {
return true
}
// MARK: - NSObject
@objc private dynamic class func keyPathsForValuesAffectingIsReady() -> Set<String> {
return ["state"]
}
@objc private dynamic class func keyPathsForValuesAffectingIsExecuting() -> Set<String> {
return ["state"]
}
@objc private dynamic class func keyPathsForValuesAffectingIsFinished() -> Set<String> {
return ["state"]
}
// MARK: - Foundation.Operation
public override final func start() {
super.start()
if isCancelled {
finish()
return
}
state = .executing
execute()
}
// MARK: - Public
/// Subclasses must implement this to perform their work and they must not
/// call `super`. The default implementation of this function throws an
/// exception.
open func execute() {
fatalError("Subclasses must implement `execute`.")
}
/// Call this function after any work is done or after a call to `cancel()`
/// to move the operation into a completed state.
public final func finish() {
state = .finished
}
}
@objc private enum OperationState: Int {
case ready
case executing
case finished
}
@jeffery812
Copy link
Author

Do think it would be better if using stateQueue.async when setting rawState?

@objc private dynamic var state: OperationState {
        get {
            return stateQueue.sync(execute: { rawState })
        }
        set {
            willChangeValue(forKey: "state")
            stateQueue.async(
                flags: .barrier,
                execute: { self.rawState = newValue })
            didChangeValue(forKey: "state")
        }
    }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment