An attempt to understand the asynchronous operation subsystem

I'm trying to start by using Operationin a third-party project, and not based on closed callbacks throughout my network code, to help eliminate nested calls. So I read this topic a bit and came across this implementation:

open class AsynchronousOperation: Operation {

    // MARK: - Properties

    private let stateQueue = DispatchQueue(label: "asynchronous.operation.state", attributes: .concurrent)

    private var rawState = OperationState.ready

    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

    private dynamic class func keyPathsForValuesAffectingIsReady() -> Set<String> {
        return ["state"]
    }

    private dynamic class func keyPathsForValuesAffectingIsExecuting() -> Set<String> {
        return ["state"]
    }

    private dynamic class func keyPathsForValuesAffectingIsFinished() -> Set<String> {
        return ["state"]
    }


    // MARK: - Foundation.Operation

    public final override 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
}

There are some implementation details of this subclass Operationthat I would like to get some help with understanding.

  1. What is the purpose of the property stateQueue? I see that you are using getand setfrom the statecalculated property, but I can not find any documentation that explains sync:flags:executeand sync:executetechniques that they use.

  2. NSObject ["state"]? - . , NSObject, class func keyPathsForValuesAffectingValue(forKey key: String) → Set<String>, , , .

+20
3

:

  1. stateQueue? , get set state, , sync:flags:execute sync:execute, .

"" , . , , . Operation, :

Multicore Considerations

... NSOperation, , . , , , -. , , . . .

, "-". "-" , (, sync, ), (, async ). WWDC 2012 , GCD XPC. , , dispatch_sync dispatch_barrier_async, Swift 3 , sync async(flags: .barrier).

:

  1. NSObject, ["state"]? - . NSObject class func keyPathsForValuesAffectingValue(forKey key: String) -> Set<String>, , .

, , state KVN isReady, isExecuting isFinished. KVN . : .

keyPathsForValuesAffectingValue . , .


, AsynchronousOperation, :

  1. super.start(). start ( ):

    , . super .

  2. @objc, Swift 4.

  3. execute, main, Operation.

  4. isReady final. isReady ( , , ).

  5. #keyPath, /.

  6. KVN dynamic. willChangeValue didChangeValue .

  7. finish, .finished, .

:

public class AsynchronousOperation: Operation {

    /// State for this operation.

    @objc private enum OperationState: Int {
        case ready
        case executing
        case finished
    }

    /// Concurrent queue for synchronizing access to 'state'.

    private let stateQueue = DispatchQueue(label: Bundle.main.bundleIdentifier! + ".rw.state", attributes: .concurrent)

    /// Private backing stored property for 'state'.

    private var _state: OperationState = .ready

    /// The state of the operation

    @objc private dynamic var state: OperationState {
        get { return stateQueue.sync { _state } }
        set { stateQueue.async(flags: .barrier) { self._state = newValue } }
    }

    // MARK: - Various 'Operation' properties

    open         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 }

    // KVN for dependent properties

    open override class func keyPathsForValuesAffectingValue(forKey key: String) -> Set<String> {
        if ["isReady", "isFinished", "isExecuting"].contains(key) {
            return [#keyPath(state)]
        }

        return super.keyPathsForValuesAffectingValue(forKey: key)
    }

    // Start

    public final override func start() {
        if isCancelled {
            state = .finished
            return
        }

        state = .executing

        main()
    }

    /// Subclasses must implement this to perform their work and they must not call 'super'. The default implementation of this function throws an exception.

    open override func main() {
        fatalError("Subclasses must implement 'main'.")
    }

    /// Call this function to finish an operation that is currently executing

    public final func finish() {
        if !isFinished { state = .finished }
    }
}
+38

, :

  1. , .finished isExecuting.

Apple:

, . , (, ), . , , Finished, YES, , NO. , .

. , "maxConcurrentOperationCount = 1" 3 AB C, , A, C , B.

+4

: stateQueue :

    return stateQueue.sync(execute: {
            rawState
    })

    stateQueue.sync(flags: .barrier, execute: {
        rawState = newValue
    })

, . isExecution, isFinished . , , stateQueue , , . , Atomic. , NSLock, Advanced NSOperations WWDC 2015 https://developer.apple.com/videos/play/wwdc2015/226/ https://developer.apple.com/sample-code/wwdc/2015/downloads/Advanced-NSOperations.zip, :

private let stateLock = NSLock()

private dynamic var state: OperationState {
    get {
        return stateLock.withCriticalScope{ rawState } 
    }
    set {
        willChangeValue(forKey: "state")

        stateLock.withCriticalScope { 
            rawState = newValue
        }
        didChangeValue(forKey: "state")
    }
}

About your second question: Its KVO notification for the read-only property isReady, isExecuting, isFinished to control the status of the operation. You can read this: http://nshipster.com/key-value-observing to the end to better understand KVO.

+2
source

Source: https://habr.com/ru/post/1675410/


All Articles