![](/img/trans.png)
[英]Sending multiple requests within an NSOperation subclass: sync or async pattern?
[英]NSOperation with a delay - is it async or sync?
我正在創建一個延遲執行閉包的NSOperation
。 操作被添加到隊列中,每次添加新操作之前,我都會取消隊列中所有現有的操作:
let myOp = SomeOperation { [weak self] in /* do something */ }
queue.cancelAllOperations()
queue.addOperation(myOp)
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
閉包中, self
為nil
:
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()
}
}
}
所以我試圖更深入地學習:
self
為nil
,因為只要DispatchQueue.main.asyncAfter…
被調用, main
方法就結束了,操作也因此被釋放。execute: doSomething
隱式捕獲/保留一個self
,所以即使在asyncAfter
之后, self
仍然存在。所以我的問題是:
start
, asynchronous
, executing
, finished
等。在我的情況下,我只需要延遲,而不是實際上做任何異步操作,我應該只在main
中執行還是應該通過實現 Apple 建議的那些方法將其作為異步操作來執行?self
保留,這聽起來不正確並且可以創建保留周期?謝謝!
您問:
- 在Apple的文檔中,它說對於並發操作,我寧願使用
start
,asynchronous
,executing
,finished
等。在我的情況下,我只需要延遲,而不是實際上做任何異步操作,我應該只在 main 中執行還是應該通過實現 Apple 建議的那些方法將其作為異步操作來執行?
首先,你正在做一些異步的事情。 即, asyncAfter
是異步的。
其次,我們像 Apple 描述的那樣包裝異步操作的原因是,整個目的是在它啟動的異步任務完成之前,操作不應該完成。 這是我們使用操作而不僅僅是 GCD 的關鍵原因之一。 也許您此時不一定需要該功能,但沒有理由在包裝的異步任務完成之前完成操作。 如果你確實推遲了操作的完成,它就會為異步任務之間的各種優雅的依賴關系打開大門。
例如,假設您創建了三個SomeOperation
,每個都依賴於前一個。 或者,也許您將它們添加到maxConcurrentOperationCount
為1
的隊列中。
此方案的意圖是您不希望在操作 1 的異步過程完成之前啟動操作 2。 如果您不將此操作設為並發操作,則操作 1、2 和 3 將立即開始。
- 我的想法是否正確,在代碼 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.