Last active
July 20, 2019 21:47
-
-
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.
This file contains hidden or 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
/* | |
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 | |
} |
This file contains hidden or 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 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