当 NSOperationQueue 中的所有其他操作完成时执行 NSOperation,无论它们是否成功完成
Executing the NSOperation when all other operations in NSOperationQueue finished no matter whether they finished successfully or not
嗨,我这里有一个奇怪的情况:
概览:
我正在开发一个用户可以启动多个操作的应用程序,所有这些操作都将 运行 在后台线程上进行,因此不会阻塞 UI。这些操作中有些是相互依赖的,有些则不是。
因此,为了确保操作仅在所有必要的依赖项操作完成执行后才执行,我使用操作的依赖项 属性。我正在使用异步操作。
这是我的实现:
import UIKit
import CoreData
import SwiftyJSON
class VMBaseOperation: NSOperation {
var finishedStatus : Bool = false
var executionStatus : Bool = false
var retryCount : Int = 0
private (set) var requestToQueue : BaseRequest? = nil
var vmOperationCompletionBlock: ((JSON?) -> Void)?
var vmOperationFailureBlock: ((WebResponseError?) -> Void)?
override init() {
super.init()
}
convenience init(withVMRequest request : BaseRequest) {
self.init()
requestToQueue = request
}
override func start() {
if self.cancelled {
self.finished = true
return
}
NSThread.detachNewThreadSelector(#selector(main), toTarget: self, withObject: nil)
self.executionStatus = true
}
override func main() {
if self.cancelled {
return
}
self.hitWebService()
}
func hitWebService(){
let webserviceManager = WebServiceManager()
webserviceManager.getResponseFromRequest(requestToQueue!) { (requset, response, data, error) in
let error = WebResponseError.checkResponse(response, request: requset, error: error)
if error != nil {
if error == WebResponseError.NO_INTERNET {
if self.vmOperationFailureBlock != nil {
self.vmOperationFailureBlock!(error)
}
self.operationFailed()
}
else{
self.retryCount += 1
if self.retryCount == 3 {
if self.vmOperationFailureBlock != nil {
self.vmOperationFailureBlock!(error)
}
self.operationFailed()
}
else{
self.hitWebService()
}
}
}
else{
if data == nil {
self.retryCount += 1
if self.retryCount == 3 {
if self.vmOperationFailureBlock != nil {
self.vmOperationFailureBlock!(nil)
}
self.operationFailed()
}
else{
self.hitWebService()
}
}
else{
let json = JSON(data: data!)
if self.vmOperationCompletionBlock != nil {
self.vmOperationCompletionBlock!(json)
}
self.operationCompleted()
}
}
}
}
override var finished: Bool {
get{
return finishedStatus
}
set{
self.willChangeValueForKey("isFinished")
finishedStatus = newValue
self.didChangeValueForKey("isFinished")
}
}
override var executing: Bool {
get{
return executionStatus
}
set{
self.willChangeValueForKey("isExecuting")
executionStatus = newValue
self.didChangeValueForKey("isExecuting")
}
}
override var asynchronous: Bool{
get{
return true
}
set{
self.willChangeValueForKey("isAsynchronous")
self.asynchronous = true
self.didChangeValueForKey("isAsynchronous")
}
}
func operationCompleted(){
self.executing = false
self.finished = true
}
func operationFailed(){
self.executing = false
self.finished = false
}
}
它的作用:
每个操作接受一个 Web 请求并尝试从服务器获取数据,如果失败,它会尝试 3 次,然后通过调用 operationFailed
方法最终将其完成状态设置为 false,然后停止所有操作依赖操作从执行 forever.On 另一方面,如果它成功,它通过调用 operationCompleted
将其完成状态更改为 true 从而触发剩余依赖操作的执行。
问题是什么:
依赖就像一种魅力。没问题。现在我需要在操作队列中的所有操作完成执行时从服务器同步数据,无论它们是否完成。
最简单的方法是创建一个操作来同步来自服务器的数据,并将其作为依赖操作添加到添加到 operationQueue 的所有操作中。
但是由于上述操作的性质,即使一个操作全部失败,它也会停止所有相关操作的执行(正如预期的那样),但是因为我来自服务器操作的同步数据也是一个相关操作,它会即使一个操作失败也永远不会执行:(
我需要什么:
在维护我上面提到的依赖性的同时,我需要知道当操作队列中的所有操作完成执行时,无论成功或失败,如何执行操作以从服务器同步数据。
这可能吗:(请帮帮我。在此先感谢。
没关系,我想通了:)
将完成状态设置为true和false会触发NSOperationQueue的KVO,从而启动或取消依赖操作的操作。
如果我希望我的操作在任何时候执行,无论依赖操作是否成功完成,我都不能使用完成标志。在这种情况下,完成标志应始终为真,以指示操作已完成执行。
但是我将如何确保对那些设置了依赖项的操作的正则链的管理,并且实际上取决于之前的操作是否成功?很简单,我在我的 NSOperationSubclass 中添加了另一个名为 finishedSuccessfully 的变量。
当操作失败时,虽然它会将完成标志设置为 true,但它会将 finishedSuccessfully 设置为 false。这将导致依赖操作启动方法被调用。
在依赖操作的start方法中,它会遍历所有的依赖操作并检查它们是否都完成了finishedSuccessfully = true。
如果是,则表示所有依赖操作已完成执行并成功完成,因此可以开始执行。另一方面,如果它们中的任何一个完成了 finishedSuccessfully = false,这意味着操作已完成执行但未能完成它应该做的任何事情因此该操作也应该停止自身并通知其依赖者它完成了 finishedSuccessfully = false.
总结:
只有在所有依赖操作执行完成后,操作才会执行,无论它们是否成功完成。
真正关心自己依赖操作执行状态的操作会检查状态,最后决定是否继续执行。
依赖链得到维护以及同步操作执行的确认:)
这是我的实现:
import UIKit
import CoreData
import SwiftyJSON
class VMBaseOperation: NSOperation {
var finishedSuccessfully : Bool = false
var finishedStatus : Bool = false
var executionStatus : Bool = false
var retryCount : Int = 0
private (set) var requestToQueue : BaseRequest? = nil
var vmOperationCompletionBlock: ((JSON?) -> Void)?
var vmOperationFailureBlock: ((WebResponseError?) -> Void)?
override init() {
super.init()
}
convenience init(withVMRequest request : BaseRequest) {
self.init()
requestToQueue = request
}
override func start() {
if self.cancelled {
self.finished = true
return
}
//those operations which actually wants to know if all its dependency operations finished successfully or not can create a subclass of this class override start method and add the below code
for operation in self.dependencies {
if (operation as! VMBaseOperation).finishedSuccessfully == false {
self.operationFailed()
return
}
}
//others can ignore.
NSThread.detachNewThreadSelector(#selector(main), toTarget: self, withObject: nil)
self.executionStatus = true
}
override func main() {
if self.cancelled {
return
}
self.hitWebService()
}
func hitWebService(){
let webserviceManager = WebServiceManager()
webserviceManager.getResponseFromRequest(requestToQueue!) { (requset, response, data, error) in
let error = WebResponseError.checkResponse(response, request: requset, error: error)
if error != nil {
if error == WebResponseError.NO_INTERNET {
if self.vmOperationFailureBlock != nil {
self.vmOperationFailureBlock!(error)
}
self.operationFailed()
}
else{
self.retryCount += 1
if self.retryCount == 3 {
if self.vmOperationFailureBlock != nil {
self.vmOperationFailureBlock!(error)
}
self.operationFailed()
}
else{
self.hitWebService()
}
}
}
else{
if data == nil {
self.retryCount += 1
if self.retryCount == 3 {
if self.vmOperationFailureBlock != nil {
self.vmOperationFailureBlock!(nil)
}
self.operationFailed()
}
else{
self.hitWebService()
}
}
else{
let json = JSON(data: data!)
if self.vmOperationCompletionBlock != nil {
self.vmOperationCompletionBlock!(json)
}
self.operationCompleted()
}
}
}
}
override var finished: Bool {
get{
return finishedStatus
}
set{
self.willChangeValueForKey("isFinished")
finishedStatus = newValue
self.didChangeValueForKey("isFinished")
}
}
override var executing: Bool {
get{
return executionStatus
}
set{
self.willChangeValueForKey("isExecuting")
executionStatus = newValue
self.didChangeValueForKey("isExecuting")
}
}
override var asynchronous: Bool{
get{
return true
}
set{
self.willChangeValueForKey("isAsynchronous")
self.asynchronous = true
self.didChangeValueForKey("isAsynchronous")
}
}
func operationCompleted(){
self.executing = false
self.finished = true
}
func operationFailed(){
self.finishedSuccessfully = false
self.operationCompleted()
}
func operationSucceeded(){
self.finishedSuccessfully = true
self.operationCompleted()
}
}
随着你的实施 operationFailed
:
func operationFailed(){
self.executing = false
self.finished = false
}
你破坏了 NSOperation 的原生逻辑:
Operation Dependencies
Dependencies are a convenient way to execute operations in a specific
order. You can add and remove dependencies for an operation using the
addDependency: and removeDependency: methods. By default, an operation
object that has dependencies is not considered ready until all of its
dependent operation objects have finished executing. Once the last
dependent operation finishes, however, the operation object becomes
ready and able to execute.
The dependencies supported by NSOperation make no distinction about
whether a dependent operation finished successfully or unsuccessfully.
(In other words, canceling an operation similarly marks it as
finished.) It is up to you to determine whether an operation with
dependencies should proceed in cases where its dependent operations
were cancelled or did not complete their task successfully. This may
require you to incorporate some additional error tracking capabilities
into your operation objects.
按照设计,如果操作失败,它应该完成。但它可以以某种方式标记自己(一些特殊的 属性 或 cancelled
)。
依赖操作应该检查是否可以启动。像下面这样的东西应该可以完成这项工作:
var requireDependencesCompletion: Bool = true
override var ready: Bool {
if requireDependencesCompletion
{
for op in self.dependencies {
if op.cancelled {
cancel()
}
}
super.ready
}
这里我们重写 ready
属性 来确定应该做什么。如果 requireDependencesCompletion
是 true
,则操作将检查其所有依赖项并在其中一个被取消时自行取消。
将 requireDependencesCompletion
设置为 true
用于您的典型操作,将 false
设置为您的 barrier 操作以便它在任何情况下都会启动。
嗨,我这里有一个奇怪的情况:
概览:
我正在开发一个用户可以启动多个操作的应用程序,所有这些操作都将 运行 在后台线程上进行,因此不会阻塞 UI。这些操作中有些是相互依赖的,有些则不是。 因此,为了确保操作仅在所有必要的依赖项操作完成执行后才执行,我使用操作的依赖项 属性。我正在使用异步操作。
这是我的实现:
import UIKit
import CoreData
import SwiftyJSON
class VMBaseOperation: NSOperation {
var finishedStatus : Bool = false
var executionStatus : Bool = false
var retryCount : Int = 0
private (set) var requestToQueue : BaseRequest? = nil
var vmOperationCompletionBlock: ((JSON?) -> Void)?
var vmOperationFailureBlock: ((WebResponseError?) -> Void)?
override init() {
super.init()
}
convenience init(withVMRequest request : BaseRequest) {
self.init()
requestToQueue = request
}
override func start() {
if self.cancelled {
self.finished = true
return
}
NSThread.detachNewThreadSelector(#selector(main), toTarget: self, withObject: nil)
self.executionStatus = true
}
override func main() {
if self.cancelled {
return
}
self.hitWebService()
}
func hitWebService(){
let webserviceManager = WebServiceManager()
webserviceManager.getResponseFromRequest(requestToQueue!) { (requset, response, data, error) in
let error = WebResponseError.checkResponse(response, request: requset, error: error)
if error != nil {
if error == WebResponseError.NO_INTERNET {
if self.vmOperationFailureBlock != nil {
self.vmOperationFailureBlock!(error)
}
self.operationFailed()
}
else{
self.retryCount += 1
if self.retryCount == 3 {
if self.vmOperationFailureBlock != nil {
self.vmOperationFailureBlock!(error)
}
self.operationFailed()
}
else{
self.hitWebService()
}
}
}
else{
if data == nil {
self.retryCount += 1
if self.retryCount == 3 {
if self.vmOperationFailureBlock != nil {
self.vmOperationFailureBlock!(nil)
}
self.operationFailed()
}
else{
self.hitWebService()
}
}
else{
let json = JSON(data: data!)
if self.vmOperationCompletionBlock != nil {
self.vmOperationCompletionBlock!(json)
}
self.operationCompleted()
}
}
}
}
override var finished: Bool {
get{
return finishedStatus
}
set{
self.willChangeValueForKey("isFinished")
finishedStatus = newValue
self.didChangeValueForKey("isFinished")
}
}
override var executing: Bool {
get{
return executionStatus
}
set{
self.willChangeValueForKey("isExecuting")
executionStatus = newValue
self.didChangeValueForKey("isExecuting")
}
}
override var asynchronous: Bool{
get{
return true
}
set{
self.willChangeValueForKey("isAsynchronous")
self.asynchronous = true
self.didChangeValueForKey("isAsynchronous")
}
}
func operationCompleted(){
self.executing = false
self.finished = true
}
func operationFailed(){
self.executing = false
self.finished = false
}
}
它的作用:
每个操作接受一个 Web 请求并尝试从服务器获取数据,如果失败,它会尝试 3 次,然后通过调用 operationFailed
方法最终将其完成状态设置为 false,然后停止所有操作依赖操作从执行 forever.On 另一方面,如果它成功,它通过调用 operationCompleted
将其完成状态更改为 true 从而触发剩余依赖操作的执行。
问题是什么:
依赖就像一种魅力。没问题。现在我需要在操作队列中的所有操作完成执行时从服务器同步数据,无论它们是否完成。
最简单的方法是创建一个操作来同步来自服务器的数据,并将其作为依赖操作添加到添加到 operationQueue 的所有操作中。
但是由于上述操作的性质,即使一个操作全部失败,它也会停止所有相关操作的执行(正如预期的那样),但是因为我来自服务器操作的同步数据也是一个相关操作,它会即使一个操作失败也永远不会执行:(
我需要什么:
在维护我上面提到的依赖性的同时,我需要知道当操作队列中的所有操作完成执行时,无论成功或失败,如何执行操作以从服务器同步数据。
这可能吗:(请帮帮我。在此先感谢。
没关系,我想通了:)
将完成状态设置为true和false会触发NSOperationQueue的KVO,从而启动或取消依赖操作的操作。
如果我希望我的操作在任何时候执行,无论依赖操作是否成功完成,我都不能使用完成标志。在这种情况下,完成标志应始终为真,以指示操作已完成执行。
但是我将如何确保对那些设置了依赖项的操作的正则链的管理,并且实际上取决于之前的操作是否成功?很简单,我在我的 NSOperationSubclass 中添加了另一个名为 finishedSuccessfully 的变量。
当操作失败时,虽然它会将完成标志设置为 true,但它会将 finishedSuccessfully 设置为 false。这将导致依赖操作启动方法被调用。
在依赖操作的start方法中,它会遍历所有的依赖操作并检查它们是否都完成了finishedSuccessfully = true。
如果是,则表示所有依赖操作已完成执行并成功完成,因此可以开始执行。另一方面,如果它们中的任何一个完成了 finishedSuccessfully = false,这意味着操作已完成执行但未能完成它应该做的任何事情因此该操作也应该停止自身并通知其依赖者它完成了 finishedSuccessfully = false.
总结:
只有在所有依赖操作执行完成后,操作才会执行,无论它们是否成功完成。
真正关心自己依赖操作执行状态的操作会检查状态,最后决定是否继续执行。
依赖链得到维护以及同步操作执行的确认:)
这是我的实现:
import UIKit
import CoreData
import SwiftyJSON
class VMBaseOperation: NSOperation {
var finishedSuccessfully : Bool = false
var finishedStatus : Bool = false
var executionStatus : Bool = false
var retryCount : Int = 0
private (set) var requestToQueue : BaseRequest? = nil
var vmOperationCompletionBlock: ((JSON?) -> Void)?
var vmOperationFailureBlock: ((WebResponseError?) -> Void)?
override init() {
super.init()
}
convenience init(withVMRequest request : BaseRequest) {
self.init()
requestToQueue = request
}
override func start() {
if self.cancelled {
self.finished = true
return
}
//those operations which actually wants to know if all its dependency operations finished successfully or not can create a subclass of this class override start method and add the below code
for operation in self.dependencies {
if (operation as! VMBaseOperation).finishedSuccessfully == false {
self.operationFailed()
return
}
}
//others can ignore.
NSThread.detachNewThreadSelector(#selector(main), toTarget: self, withObject: nil)
self.executionStatus = true
}
override func main() {
if self.cancelled {
return
}
self.hitWebService()
}
func hitWebService(){
let webserviceManager = WebServiceManager()
webserviceManager.getResponseFromRequest(requestToQueue!) { (requset, response, data, error) in
let error = WebResponseError.checkResponse(response, request: requset, error: error)
if error != nil {
if error == WebResponseError.NO_INTERNET {
if self.vmOperationFailureBlock != nil {
self.vmOperationFailureBlock!(error)
}
self.operationFailed()
}
else{
self.retryCount += 1
if self.retryCount == 3 {
if self.vmOperationFailureBlock != nil {
self.vmOperationFailureBlock!(error)
}
self.operationFailed()
}
else{
self.hitWebService()
}
}
}
else{
if data == nil {
self.retryCount += 1
if self.retryCount == 3 {
if self.vmOperationFailureBlock != nil {
self.vmOperationFailureBlock!(nil)
}
self.operationFailed()
}
else{
self.hitWebService()
}
}
else{
let json = JSON(data: data!)
if self.vmOperationCompletionBlock != nil {
self.vmOperationCompletionBlock!(json)
}
self.operationCompleted()
}
}
}
}
override var finished: Bool {
get{
return finishedStatus
}
set{
self.willChangeValueForKey("isFinished")
finishedStatus = newValue
self.didChangeValueForKey("isFinished")
}
}
override var executing: Bool {
get{
return executionStatus
}
set{
self.willChangeValueForKey("isExecuting")
executionStatus = newValue
self.didChangeValueForKey("isExecuting")
}
}
override var asynchronous: Bool{
get{
return true
}
set{
self.willChangeValueForKey("isAsynchronous")
self.asynchronous = true
self.didChangeValueForKey("isAsynchronous")
}
}
func operationCompleted(){
self.executing = false
self.finished = true
}
func operationFailed(){
self.finishedSuccessfully = false
self.operationCompleted()
}
func operationSucceeded(){
self.finishedSuccessfully = true
self.operationCompleted()
}
}
随着你的实施 operationFailed
:
func operationFailed(){
self.executing = false
self.finished = false
}
你破坏了 NSOperation 的原生逻辑:
Operation Dependencies
Dependencies are a convenient way to execute operations in a specific order. You can add and remove dependencies for an operation using the addDependency: and removeDependency: methods. By default, an operation object that has dependencies is not considered ready until all of its dependent operation objects have finished executing. Once the last dependent operation finishes, however, the operation object becomes ready and able to execute.
The dependencies supported by NSOperation make no distinction about whether a dependent operation finished successfully or unsuccessfully. (In other words, canceling an operation similarly marks it as finished.) It is up to you to determine whether an operation with dependencies should proceed in cases where its dependent operations were cancelled or did not complete their task successfully. This may require you to incorporate some additional error tracking capabilities into your operation objects.
按照设计,如果操作失败,它应该完成。但它可以以某种方式标记自己(一些特殊的 属性 或 cancelled
)。
依赖操作应该检查是否可以启动。像下面这样的东西应该可以完成这项工作:
var requireDependencesCompletion: Bool = true
override var ready: Bool {
if requireDependencesCompletion
{
for op in self.dependencies {
if op.cancelled {
cancel()
}
}
super.ready
}
这里我们重写 ready
属性 来确定应该做什么。如果 requireDependencesCompletion
是 true
,则操作将检查其所有依赖项并在其中一个被取消时自行取消。
将 requireDependencesCompletion
设置为 true
用于您的典型操作,将 false
设置为您的 barrier 操作以便它在任何情况下都会启动。