Overview
- 有一个异步操作子类
- 将此操作添加到队列中。
- 我在开始之前取消了此操作。
运行时错误/警告:
SomeOperation went isFinished=YES without being started by the queue it is in
问题:
- 这是可以忽略的事情还是很严重的事情?
- 如何解决这个问题?
- 最后提供的解决方法/解决方案有效吗?
Code:
public class SomeOperation : AsyncOperation {
//MARK: Start
public override func start() {
isExecuting = true
guard !isCancelled else {
markAsCompleted() //isExecuting = false, isFinished = true
return
}
doSomethingAsynchronously { [weak self] in
self?.markAsCompleted() //isExecuting = false, isFinished = true
}
}
//MARK: Cancel
public override func cancel() {
super.cancel()
markAsCompleted() //isExecuting = false, isFinished = true
}
}
添加到队列和取消:
//someOperation is a property in a class
if let someOperation = someOperation {
queue.addOperation(someOperation)
}
//Based on some condition cancelling it
someOperation?.cancel()
这是有效的解决方案吗?
public override func cancel() {
isExecuting = true //Just in case the operation was cancelled before starting
super.cancel()
markAsCompleted()
}
Note:
-
markAsCompleted
sets isExecuting = false
and isFinished = true
-
isExecuting
, isFinished
是同步的属性KVO
关键问题是你的markAsCompleted
正在触发isFinished
当操作不isExecuting
。我建议你解决这个问题markAsCompleted
仅在以下情况下才执行此操作isExecuting
是真的。这减轻了子类进行任何复杂状态测试以确定它们是否需要转换到的负担isFinished
or not.
话虽如此,我在编写可取消的异步操作时看到了三种基本模式:
-
如果我正在处理某种模式,其中取消任务将阻止它将执行操作转换为isFinished
state.
在那种情况下,我必须有cancel
实现手动完成正在执行的操作。例如:
class FiveSecondOperation: AsynchronousOperation {
var block: DispatchWorkItem?
override func main() {
block = DispatchWorkItem { [weak self] in
self?.finish()
self?.block = nil
}
DispatchQueue.main.asyncAfter(deadline: .now() + 5, execute: block!)
}
override func cancel() {
super.cancel()
if isExecuting {
block?.cancel()
finish()
}
}
}
专注于cancel
实施,因为如果我取消DispatchWorkItem
它不会完成操作,因此我需要确保cancel
将显式地完成操作本身。
-
有时,当您取消某些异步任务时,它会自动为您调用其完成处理程序,在这种情况下cancel
除了取消该任务并调用 super 之外不需要执行任何操作。例如:
class GetOperation: AsynchronousOperation {
var url: URL
weak var task: URLSessionTask?
init(url: URL) {
self.url = url
super.init()
}
override func main() {
let task = URLSession.shared.dataTask(with: url) { data, _, error in
defer { self.finish() } // make sure to finish the operation
// process `data` & `error` here
}
task.resume()
self.task = task
}
override func cancel() {
super.cancel()
task?.cancel()
}
}
再次,重点关注cancel
,在这种情况下我们不触及“完成”状态,而只是取消dataTask
(即使您取消请求,它也会调用其完成处理程序)并调用super
执行。
-
第三种情况是您有一些定期检查的操作isCancelled
状态。在这种情况下,您不必实施cancel
根本没有,因为默认行为就足够了。例如:
class DisplayLinkOperation: AsynchronousOperation {
private weak var displayLink: CADisplayLink?
private var startTime: CFTimeInterval!
private let duration: CFTimeInterval = 2
override func main() {
startTime = CACurrentMediaTime()
let displayLink = CADisplayLink(target: self, selector: #selector(handleDisplayLink(_:)))
displayLink.add(to: .main, forMode: .commonModes)
self.displayLink = displayLink
}
@objc func handleDisplayLink(_ displayLink: CADisplayLink) {
let percentComplete = (CACurrentMediaTime() - startTime) / duration
if percentComplete >= 1.0 || isCancelled {
displayLink.invalidate()
finish()
}
// now do some UI update based upon `elapsed`
}
}
在这种情况下,我在操作中包装了显示链接,以便我可以管理依赖项和/或将显示链接封装在一个方便的对象中,我不必实现cancel
根本没有,因为默认实现会更新isCancelled
对我来说,我可以检查一下。
这是三个基本的cancel
我通常看到的模式。话虽如此,更新markAsCompleted
仅触发isFinished
if isExecuting
是一个很好的安全检查,以确保您永远不会遇到您所描述的问题。
顺便说一句,AsynchronousOperation
我在上面的例子中使用的内容如下,改编自尝试理解异步操作子类。顺便说一句,你叫什么markAsCompleted
叫做finish
,听起来你正在触发isFinished
and isExecuting
KVO 通过不同的机制,但思想基本相同。只需在触发前检查当前状态isFinished
KVO:
open 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 rawState: OperationState = .ready
/// The state of the operation
@objc private dynamic var state: OperationState {
get { return stateQueue.sync { rawState } }
set { stateQueue.sync(flags: .barrier) { rawState = 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 }
// MARK: - 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)
}
// MARK: - Foundation.Operation
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 isExecuting { state = .finished }
}
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)