gpt4 book ai didi

ios - 操作完成了 isFinished=YES 没有被它所在的队列启动

转载 作者:搜寻专家 更新时间:2023-11-01 05:49:36 24 4
gpt4 key购买 nike

概览

  • 有异步操作子类
  • 将此操作添加到队列中。
  • 我在开始之前取消了这个操作。

运行时错误/警告:

SomeOperation isFinished=YES 没有被它所在的队列启动

问题:

  1. 这是可以忽略的事情还是严重的事情?
  2. 如何解决这个问题?
  3. 最后提供的解决方法/解决方案是否有效?

代码:

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()
}

注意事项:

  • markAsCompleted 设置 isExecuting = falseisFinished = true
  • isExecuting, isFinished 是同步的属性 KVO

最佳答案

关键问题是您的markAsCompleted 在操作未isExecuting 时触发isFinished。我建议您将 markAsCompleted 修改为仅在 isExecuting 为真时执行此操作。这减轻了子类执行任何复杂状态测试以确定它们是否需要转换到 isFinished 的负担。

话虽如此,我在编写可取消异步操作时看到了三种基本模式:

  1. 如果我正在处理某种模式,其中取消任务将阻止它把正在执行的操作转换为 isFinished 状态。

    在那种情况下,我必须让 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 将明确地完成操作本身。

  2. 有时,当您取消一些异步任务时,它会自动为您调用其完成处理程序,在这种情况下,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,在这种情况下我们不触及“finished”状态,而只是取消dataTask(它将调用它的完成处理程序,即使您取消请求)并调用 super 实现。

  3. 第三种情况是您有一些定期检查 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 以仅在 isExecuting 时触发 isFinished 是一个很好的安全检查,以确保您永远不会遇到您描述的问题.


顺便说一句,我在上面的例子中使用的AsynchronousOperation如下,改编自Trying to Understand Asynchronous Operation Subclass .顺便说一句,你所谓的 markAsCompleted 被称为 finish,听起来你正在触发 isFinishedisExecuting 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 }
}
}

关于ios - 操作完成了 isFinished=YES 没有被它所在的队列启动,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48137896/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com