-
-
Save jeffery812/ca19daa7e344c298d1680fb760760e25 to your computer and use it in GitHub Desktop.
Concurrent NSOperation in Swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Do think it would be better if using
stateQueue.async
when setting rawState?