无论NSOperationQueue中的所有其他操作是否成功完成,都执行NSOperation

时间:2016-08-29 08:02:30

标签: ios swift nsoperation nsoperationqueue

嗨,我有一个奇怪的情况:

概述

我正在开发一个应用程序,用户可以在其中启动多个操作,所有这些操作都将在后台线程上运行,因此不会阻止UI。其中一些操作相互依赖,而另一些则不相关。 因此,为了确保只有在使用Operation的dependency属性完成所有必要的依赖操作后才能执行操作。我正在使用异步操作。

这是我的实施:

<system.net>
<mailSettings>
  <smtp deliveryMethod="Network">
    <network enableSsl="true" />
  </smtp>
</mailSettings>

它的作用:

每个操作都会接受一个Web请求并尝试从服务器获取数据,如果失败,它会尝试3次,最后通过调用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 } } 方法将其完成状态设置为false,然后停止所有相关操作永远执行。另一方面,如果成功,它通过调用operationFailed将其完成状态更改为true,从而触发执行剩余的相关操作。

问题是什么:

依赖性就像魅力一样。没问题。现在,无论操作队列中的所有操作是否成功完成,我都需要从服务器同步数据。

最简单的方法是创建一个操作来同步服务器中的数据,并将其作为依赖操作添加到添加到operationQueue的所有操作中。

但是由于上述操作的性质,即使一个操作失败也会停止所有相关操作的执行(如预期的那样),但由于我的服务器操作的同步数据也是一个依赖操作,它永远不会执行即使一次操作失败:(

我需要什么:

在保持上面提到的依赖关系的同时,我需要知道当操作队列中的所有操作完成执行时,无论它们是成功还是失败,如何执行从服务器同步数据的操作。

这是否可能:(请帮帮我。提前致谢。

2 个答案:

答案 0 :(得分:2)

别介意我弄明白了:)

将完成状态设置为true和false会触发NSOperationQueue的KVO,从而启动或取消相关操作的操作。

如果我希望我的操作在任何时候执行,无论依赖操作是否成功完成,我都无法使用完成标志。在这种情况下,finished flag应该始终为true,表示操作已完成执行。

但是我如何确保对那些具有依赖关系的操作管理正派链,并且实际上取决于先前的操作是否成功?简单的我在我的NSOperationSubclass中添加了另一个名为finishedSuccessfully的变量。

当操作失败但将finished标志设置为true时,它会将finishedSuccessfully设置为false。这将导致相关操作start方法被调用。

在依赖操作的start方法中,它将遍历所有依赖操作,并且所有这些操作都以finishedSuccessfully = true完成。

如果是,则表示所有依赖操作都已完成执行并成功完成,因此可以开始执行。另一方面,如果其中任何一个已经完成成功=假,这意味着操作已完成执行但未能执行它应该执行的任何操作,因此此操作也应该自行停止并通知其依赖者它已完成finishSuccessfully = false。

摘要:

  1. 只有在所有依赖操作执行完毕后,无论操作是否成功,操作都将执行。

  2. 实际关注其依赖操作执行状态的操作将检查状态,然后最终决定是否继续执行。

  3. 维护依赖链以及确认同步操作也执行:)

    这是我的实施:

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

答案 1 :(得分:2)

使用您的实施operationFailed

func operationFailed(){
    self.executing = false
    self.finished = false
}

你打破了NSOperation的原生逻辑:

  

操作依赖性

     

依赖关系是在特定操作中执行操作的便捷方式   订购。您可以使用。添加和删除操作的依赖项   addDependency:和removeDependency:方法。默认情况下,一个操作   具有依赖关系的对象在其全部之前不被认为是准备好的   依赖操作对象已完成执行。一旦到最后   然而,依赖操作结束,操作对象变为   准备好并且能够执行。

     

NSOperation支持的依赖关系没有区别   依赖操作是成功完成还是失败。   (换句话说,取消操作同样将其标记为   完成。)由您决定是否使用进行操作   依赖关系应该在其依赖操作的情况下进行   已取消或未成功完成任务。这可能   要求您加入一些额外的错误跟踪功能   进入你的操作对象。

如果操作失败,应按设计完成。但它可能以某种方式标记自己(某些特殊属性或cancelled)。

依赖操作应检查是否可以启动。像下面这样的东西应该做的工作:

var requireDependencesCompletion: Bool = true

override var ready: Bool { 
    if requireDependencesCompletion
    {
        for op in self.dependencies {
            if op.cancelled {
                cancel()
        }
    }
    super.ready
}

这里我们覆盖ready属性以确定应该做什么。如果requireDependencesCompletiontrue,则操作将检查其所有依赖关系,并在其中一个被取消时取消自身。

requireDependencesCompletion设置为true进行典型操作,将false设置为障碍操作,以便在任何情况下都可以启动。