簡體   English   中英

NSOperation 有延遲 - 它是異步的還是同步的?

[英]NSOperation with a delay - is it async or sync?

我正在創建一個延遲執行閉包的NSOperation 操作被添加到隊列中,每次添加新操作之前,我都會取消隊列中所有現有的操作:

let myOp = SomeOperation { [weak self] in /* do something */ }
queue.cancelAllOperations()
queue.addOperation(myOp)

操作代碼 1

final class SomeOperation: Operation {

    private let closure: () -> Void

    init(closure: @escaping () -> Void) {
        self.closure = closure
    }

    override func main() {
        if isCancelled {
            return
        }
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.3, execute: doSomething)
    }

    private func doSomething() {
        guard isCancelled == false else {
            return
        }
        closure()
    }
}

雖然上面的代碼有效,但下面的代碼無效。 DispatchQueue閉包中, selfnil

操作代碼 2

final class SomeOperation: Operation {

    private let closure: () -> Void

    init(closure: @escaping () -> Void) {
        self.closure = closure
    }

    override func main() {
        if isCancelled {
            return
        }
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { [weak self] in
            guard let self = self else { return }
            guard isCancelled == false else { return }
            self.closure()
        }
    }
}

所以我試圖更深入地學習:

  1. 在代碼 2 中, selfnil ,因為只要DispatchQueue.main.asyncAfter…被調用, main方法就結束了,操作也因此被釋放。
  2. 代碼 1 有效,因為execute: doSomething隱式捕獲/保留一個self ,所以即使在asyncAfter之后, self仍然存在。

所以我的問題是:

  1. 在Apple的文檔中,它說對於並發操作,我寧願使用startasynchronousexecutingfinished等。在我的情況下,我只需要延遲,而不是實際上做任何異步操作,我應該只在main中執行還是應該通過實現 Apple 建議的那些方法將其作為異步操作來執行?
  2. 我的想法是否正確,在代碼 1 中有一個隱含的self保留,這聽起來不正確並且可以創建保留周期?

謝謝!

您問:

  1. 在Apple的文檔中,它說對於並發操作,我寧願使用startasynchronousexecutingfinished等。在我的情況下,我只需要延遲,而不是實際上做任何異步操作,我應該只在 main 中執行還是應該通過實現 Apple 建議的那些方法將其作為異步操作來執行?

首先,你正在做一些異步的事情。 即, asyncAfter是異步的。

其次,我們像 Apple 描述的那樣包裝異步操作的原因是,整個目的是在它啟動的異步任務完成之前,操作不應該完成。 這是我們使用操作而不僅僅是 GCD 的關鍵原因之一。 也許您此時不一定需要該功能,但沒有理由在包裝的異步任務完成之前完成操作。 如果你確實推遲了操作的完成,它就會為異步任務之間的各種優雅的依賴關系打開大門。

例如,假設您創建了三個SomeOperation ,每個都依賴於前一個。 或者,也許您將它們添加到maxConcurrentOperationCount1的隊列中。

此方案的意圖是您不希望在操作 1 的異步過程完成之前啟動操作 2。 如果您不將此操作設為並發操作,則操作 1、2 和 3 將立即開始。

  1. 我的想法是否正確,在代碼 1 中有一個隱含的自我保留,這聽起來不正確並且可以創建保留周期?

關於強引用周期問題,讓我們看一下您的第一個示例。 雖然操作的創建者使用[weak self]捕獲列表是謹慎的,但它不應該是必需的。 良好的操作設計(或任何使用異步調用閉包的東西)是讓它在不再需要時釋放閉包:

class SomeOperation2: Operation {
    private var closure: (() -> Void)?
    
    init(closure: @escaping () -> Void) {
        self.closure = closure
    }
    
    override func main() {
        if isCancelled {
            return
        }
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.3, execute: doSomething)
    }
    
    override func cancel() {
        closure = nil
        
        super.cancel()
    }
    
    private func doSomething() {
        guard !isCancelled else {
            return
        }
        
        closure?()
        closure = nil
    }
} 

這並不意味着調用者不應該使用[weak self]捕獲列表,只是該操作不再需要它,並且在完成閉包時將解決任何強引用循環。

[注意,在上面,我省略了變量的同步,以保持簡單。 但是您需要同步對它的訪問以確保線程安全設計。]

但是這種設計引出了一個問題,即為什么要保持asyncAfter計划,即使在取消操作后仍會觸發。 最好取消它,通過將閉包包裝在可以取消的DispatchWorkItem中,例如:

class SomeOperation: Operation {
    private var item: DispatchWorkItem!
    
    init(closure: @escaping () -> Void) {
        super.init()
        
        item = DispatchWorkItem { [weak self] in
            closure()
            self?.item = nil
        }
    }
    
    override func main() {
        if isCancelled { return }
        
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.3, execute: item)
    }
    
    override func cancel() {
        item?.cancel()
        item = nil

        super.cancel()
    }
}

概述了 memory 問題后,我們應該注意這一切可能都沒有實際意義,因為您可能應該按照文檔中的說明將其設為並發操作(使用所有自定義 KVO)。 例如,

class SomeOperation: AsynchronousOperation {
    private var item: DispatchWorkItem!

    init(closure: @escaping () -> Void) {
        super.init()

        item = DispatchWorkItem { [weak self] in
            closure()
            self?.item = nil
            self?.complete()
        }
    }

    override func main() {
        if isCancelled { return }

        synchronized {
            DispatchQueue.main.asyncAfter(deadline: .now() + 3, execute: item)
        }
    }

    override func cancel() {
        super.cancel()

        synchronized {
            item?.cancel()
            item = nil
        }
    }
}

上面使用了一個異步操作基礎 class ,它 (a) 執行必要的 KVO 通知; (b) 是線程安全的。 這是如何實現的一個隨機示例:

/// Asynchronous Operation base class
///
/// This class performs all of the necessary KVN of `isFinished` and
/// `isExecuting` for a concurrent `NSOperation` subclass. So, to developer
/// a concurrent NSOperation subclass, you instead subclass this class which:
///
/// - must override `main()` with the tasks that initiate the asynchronous task;
///
/// - must call `complete()` function when the asynchronous task is done;
///
/// - optionally, periodically check `self.cancelled` status, performing any clean-up
///   necessary and then ensuring that `complete()` is called; or
///   override `cancel` method, calling `super.cancel()` and then cleaning-up
///   and ensuring `complete()` is called.

public class AsynchronousOperation: Operation {

    private let lock = NSLock()

    private var _executing: Bool = false
    override private(set) public var isExecuting: Bool {
        get {
            synchronized { _executing }
        }
        set {
            willChangeValue(forKey: #keyPath(isExecuting))
            synchronized { _executing = newValue }
            didChangeValue(forKey: #keyPath(isExecuting))
        }
    }

    private var _finished: Bool = false
    override private(set) public var isFinished: Bool {
        get {
            synchronized { _finished }
        }
        set {
            willChangeValue(forKey: #keyPath(isFinished))
            synchronized { _finished = newValue }
            didChangeValue(forKey: #keyPath(isFinished))
        }
    }

    override public var isAsynchronous: Bool { return true }

    /// Complete the operation
    ///
    /// This will result in the appropriate KVN of isFinished and isExecuting

    public func complete() {
        if isExecuting {
            isExecuting = false
            isFinished = true
        }
    }

    public override func cancel() {
        super.cancel()

        complete()
    }

    override public func start() {
        if isCancelled {
            isFinished = true
            return
        }

        isExecuting = true

        main()
    }

    override public func main() {
        fatalError("subclasses must override `main`")
    }

    public func synchronized<T>(block: () throws -> T) rethrows -> T {
        try lock.synchronized { try block() }
    }
}

extension NSLocking {
    public func synchronized<T>(block: () throws -> T) rethrows -> T {
        lock()
        defer { unlock() }
        return try block()
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM