Skip to content

Instantly share code, notes, and snippets.

@robertherdzik
Last active July 20, 2019 21:47
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save robertherdzik/d38ec255f585417189aecfeece6f6711 to your computer and use it in GitHub Desktop.
Save robertherdzik/d38ec255f585417189aecfeece6f6711 to your computer and use it in GitHub Desktop.
If you need simple class for you async Operations, you landed in the good place. Simplified version of base Operation class form Apple Earthquake 2015 example.
/*
Copyright (C) 2015 Apple Inc. All Rights Reserved.
See LICENSE.txt for this sample’s licensing information
Abstract:
This file contains the foundational subclass of NSOperation.
*/
import Foundation
extension NSLock {
func withCriticalScope<T>( block: () -> T) -> T {
lock()
let value = block()
unlock()
return value
}
}
class RHOperation: Operation {
fileprivate enum State: Int, Comparable {
case Initialized
/// The `Operation` is executing.
case Executing
/// The `Operation` has finished executing.
case Finished
func canTransitionToState(target: State) -> Bool {
switch (self, target) {
case (.Initialized, .Finished):
return true
case (.Initialized, .Executing):
return true
case (.Executing, .Finished):
return true
default:
return false
}
}
}
/// A lock to guard reads and writes to the `_state` property
private let stateLock = NSLock()
/// Private storage for the `state` property that will be KVO observed.
private var _state = State.Initialized
private var state: State {
get {
return stateLock.withCriticalScope {
_state
}
}
set(newState) {
/*
It's important to note that the KVO notifications are NOT called from inside
the lock. If they were, the app would deadlock, because in the middle of
calling the `didChangeValueForKey()` method, the observers try to access
properties like "isReady" or "isFinished". Since those methods also
acquire the lock, then we'd be stuck waiting on our own lock. It's the
classic definition of deadlock.
*/
willChangeValue(forKey: "state")
stateLock.withCriticalScope { Void -> Void in
guard _state != .Finished else {
return
}
assert(_state.canTransitionToState(target: newState), "Performing invalid state transition.")
_state = newState
}
didChangeValue(forKey: "state")
}
}
@objc
class func keyPathsForValuesAffectingIsExecuting() -> Set<NSObject> {
return ["state" as NSObject]
}
@objc
class func keyPathsForValuesAffectingIsFinished() -> Set<NSObject> {
return ["state" as NSObject]
}
override var isAsynchronous: Bool {
return true
}
override var isExecuting: Bool {
return state == .Executing
}
override var isFinished: Bool {
return state == .Finished
}
override func start() {
state = .Executing
execute()
}
func execute() {
print("\(type(of: self)) must override `execute()`.")
finish()
}
private var hasFinishedAlready = false
final func finish() {
if !hasFinishedAlready {
hasFinishedAlready = true
state = .Finished
}
}
}
// Simple operator functions to simplify the assertions used above.
private func <(lhs: RHOperation.State, rhs: RHOperation.State) -> Bool {
return lhs.rawValue < rhs.rawValue
}
private func ==(lhs: RHOperation.State, rhs: RHOperation.State) -> Bool {
return lhs.rawValue == rhs.rawValue
}
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var queue: OperationQueue = OperationQueue()
func applicationDidFinishLaunching(_ application: UIApplication) {
let sleepOp = SleepOperation()
let parseOp = ParseOperation()
let completionOp = BlockOperation {
print("completionOp " + sleepOp.result)
}
completionOp.addDependency(sleepOp)
completionOp.addDependency(parseOp)
queue.addOperations([sleepOp, parseOp, completionOp], waitUntilFinished: true)
print("πŸ‘")
}
}
class SleepOperation: RHOperation {
var result = ""
override func execute() {
desc(with: "BEGIN πŸ˜‡")
Thread.sleep(forTimeInterval: 1)
desc(with: "πŸ˜‡πŸ˜΄")
result = "Finished"
finish()
}
}
class ParseOperation: RHOperation {
override func execute() {
desc(with: "BEGIN 😍")
desc(with: "😍😴")
finish()
}
}
extension NSObject {
func desc(with text: String) {
print(description + text)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment