【问题标题】:Why my NSOperation is not cancelling?为什么我的 NSOperation 没有取消?
【发布时间】:2018-11-26 23:24:25
【问题描述】:

我有这段代码可以将NSOperation 实例添加到队列中

let operation = NSBlockOperation()
operation.addExecutionBlock({
    self.asyncMethod() { (result, error) in
        if operation.cancelled {
            return
        }

        // etc
    }
})
operationQueue.addOperation(operation)

当用户离开触发上述代码的视图时,我取消操作

operationQueue.cancelAllOperations()

在测试取消时,我 100% 确定取消在异步方法返回之前执行,所以我希望 operation.cancelled 为真。不幸的是,这没有发生,我无法理解为什么

我正在对 viewWillDisappear 执行取消操作

编辑

asyncMethod 包含在不同线程中运行的网络操作。这就是回调存在的原因:处理网络操作返回。网络操作在类层次结构的深处执行,但我想在根级别处理 NSOperations。

【问题讨论】:

  • 你必须确定,在测试之前 operation.cancelled 是真的,而不仅仅是在异步方法返回之前......
  • @user3441734:“测试前”是什么意思?在异步方法启动之前取消操作是不可能的,因为没有逻辑。如果之前取消,它根本不会运行。
  • "我 100% 确定取消在异步方法返回之前执行,所以我希望 operation.cancelled 为真"。 async 方法将返回,即使 operation.cancelled 为 false。它只是不'早'返回。
  • @user3441734:对不起,我不明白你的意思。我不希望异步方法提前返回。我只是想避免通过operation.cancelled check 执行回调逻辑。
  • 避免执行?这是您的责任....为此,您测试 operation.cancelled 是真还是假,不是吗?背后没有奇迹,如果 operation.cancelled 是真的,不要继续工作(你可以一次又一次地测试它......在你的代码中的任何地方,并从工作中返回(完成它)'早'

标签: swift nsoperation


【解决方案1】:

调用this对象的cancel方法设置this的值 属性为 YES。一旦取消,操作必须移动到完成 状态。

取消操作不会主动停止接收者的代码 执行。一个操作对象负责调用这个方法 如果方法返回 YES,则周期性地自行停止。

在执行任何操作之前,您应该始终检查此属性的值 努力完成操作的任务,这通常意味着 在自定义 main 方法的开头检查它。这是 可能在开始执行之前取消操作 或在它执行时的任何时间。因此,检查值 您的主要方法的开始(并在整个过程中定期 方法)让您在操作完成时尽快退出 取消。

import Foundation

let operation1 = NSBlockOperation()
let operation2 = NSBlockOperation()
let queue = NSOperationQueue()
operation1.addExecutionBlock { () -> Void in
    repeat {
        usleep(10000)
        print(".", terminator: "")
    } while !operation1.cancelled
}
operation2.addExecutionBlock { () -> Void in
    repeat {
        usleep(15000)
        print("-", terminator: "")
    } while !operation2.cancelled
}
queue.addOperation(operation1)
queue.addOperation(operation2)
sleep(1)
queue.cancelAllOperations()

在操场上试试这个简单的例子。

如果运行另一个异步代码真的很重要,试试这个

operation.addExecutionBlock({
if operation.cancelled {
            return
        }    
self.asyncMethod() { (result, error) in


        // etc
    }
})

【讨论】:

  • 请正确阅读我的问题。我正在检查cancelled 的值。我的问题是总是false,即使我取消了操作
  • 如果你的 self.asyncMethod() 真的是异步的,它会立即返回并且执行块也会立即退出。我用非常基本的示例更新了我的答案,如何使用 NSOperation 和 NSOperationQueue
  • 感谢您将所有这些放在一起,但我看不出我的代码和您的代码有任何区别。我在操作取消和异步方法回调后将调试器上线,我验证了取消在if operation.cancelled 之前执行,但取消仍保留在false
  • 你为什么在操作块内执行另一个异步代码?在女巫队列上,这段代码正在运行吗?在您的关闭中,该值反映了捕获的 operation.cancelled 值并且永远不会改变......如果有必要“按原样”运行该代码,您必须在将作业分派到另一个队列之前检查 operation.cancelled 值跨度>
  • 异步方法通过第三方组件访问网络资源,我无法控制它。我需要检查回调中的操作状态或寻找不同的方法。在我的场景中,在调用异步方法之前检查状态是没有意义的。谢谢
【解决方案2】:

这是因为你做错了。执行后取消操作。 检查此代码,块在一个后台线程中执行。在执行开始之前 - 操作取消,从队列中删除第一个块。

斯威夫特 4

let operationQueue = OperationQueue()
operationQueue.qualityOfService = .background

let ob1 = BlockOperation {
    print("ExecutionBlock 1. Executed!")
}

let ob2 = BlockOperation {
    print("ExecutionBlock 2. Executed!")
}

operationQueue.addOperation(ob1)
operationQueue.addOperation(ob2)

ob1.cancel()

// ExecutionBlock 2. Executed!

斯威夫特 2

let operationQueue = NSOperationQueue()
operationQueue.qualityOfService = .Background

let ob1 = NSBlockOperation()
ob1.addExecutionBlock {
    print("ExecutionBlock 1. Executed!")
}

let ob2 = NSBlockOperation()
ob2.addExecutionBlock {
    print("ExecutionBlock 2. Executed!")
}

operationQueue.addOperation(ob1)
operationQueue.addOperation(ob2)

ob1.cancel()

// ExecutionBlock 2. Executed!

【讨论】:

    【解决方案3】:

    操作不会等待您的asyncMethod 完成。因此,如果您将其添加到队列中,它会立即返回。这是因为您将异步网络操作包装在异步 NSOperation 中。

    NSOperation 旨在提供更高级的异步处理,而不是仅调用 performSelectorInBackground。这意味着NSOperation 用于在后台带来复杂且长时间运行的操作,而不是阻塞主线程。可以在这里找到一篇常用的NSOperation 的好文章:

    http://www.raywenderlich.com/19788/how-to-use-nsoperations-and-nsoperationqueues

    对于您的特定用例,在这里使用 NSOperation 是没有意义的,您应该取消正在运行的网络请求。

    【讨论】:

      【解决方案4】:

      将异步函数放入带有NSBlockOperation 的块中是没有意义的。您可能想要的是NSOperation 的正确子类,作为执行异步工作负载的并发操作。然而,正确子类化 NSOperation 并不像它应该的那么容易。

      您可以在这里查看reusable subclass for NSOperation 以获取示例实现。

      【讨论】:

        【解决方案5】:

        我不是 100% 确定您在寻找什么,但也许您需要将操作作为参数传递到 asyncMethod() 并在那里测试取消状态?

        operation.addExecutionBlock({
          asyncMethod(operation) { (result, error) in
          // Result code
          }
        })
        operationQueue.addOperation(operation)
        
        func asyncMethod(operation: NSBlockOperation, fun: ((Any, Any)->Void)) {
          // Do stuff...
          if operation.cancelled {
            // Do something...
            return // <- Or whatever makes senes
          }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-10-12
          • 1970-01-01
          • 1970-01-01
          • 2017-03-15
          • 1970-01-01
          相关资源
          最近更新 更多