【问题标题】:Understanding sequence of operations with dependency in Swift理解 Swift 中具有依赖关系的操作序列
【发布时间】:2017-02-14 09:50:34
【问题描述】:

参考https://developer.apple.com/reference/foundation/operation,我将 Playground 设置为 -

class myOperation1 : Operation {
    override func main() {
        print("op1 working....")
    }
}

class myOperation2 : Operation {
    override func main() {
        print("op2 working....")
    }
}

let op1 = myOperation1()
let op2 = myOperation2()

op1.completionBlock = {
    print("op1 finished")
}

op2.completionBlock = {
    print("op2 finished")
}

op2.addDependency(op1)

let opsQue = OperationQueue()
opsQue.addOperation(op1)
opsQue.addOperation(op2)

控制台日志是 -

op1 working....
op2 working....
op1 finished
op2 finished

我们不应该期望输出是依赖的结果吗? -

op1 working....
op1 finished
op2 working....
op2 finished

使用相同的结果 - opsQue.addOperations([op1, op2], waitUntilFinished: true)

op1 working....
op2 working....
op1 finished
op2 finished

【问题讨论】:

  • 为了避免代码排序,可以使用opsQue.addOperations([op1, op2], waitUntilFinished: true)
  • 并尝试依赖和不依赖
  • @WeiJay:也许我不应该在 Playground 中观察到这种行为,在实际项目中它应该可以按预期工作
  • @BaSha 不,在操场上检查它应该反映相同的输出,如果你想重新测试它,只需点击蓝色箭头按钮(执行操场),这将重新运行代码,你每次运行都会看到不同的结果:)
  • @AhmadF: 没错,同样的依赖,我看到不同的结果:)

标签: ios swift nsoperation nsoperationqueue


【解决方案1】:

事实上,我无法确定为什么您的代码无法正常工作的原因究竟是什么,但我想出了 3 个解决方法来实现您的目标:

如果您期望输出应该总是是:

op1 working....
op1 finished
op2 working....
op2 finished

然后:

1-您可能希望将第二个操作添加到第一个的完成块中的队列中,如下所示:

class myOperation1 : Operation {
    override func main() {
        print("op1 working....")
    }
}

class myOperation2 : Operation {
    override func main() {
        print("op2 working....")
    }
}

let opsQue = OperationQueue()

let op1 = myOperation1()

op1.completionBlock = {
    print("op1 finished")

    opsQue.addOperation(op2)
}

let op2 = myOperation2()

op2.completionBlock = {
    print("op2 finished")
}

opsQue.addOperation(op1)

2- 设置maxConcurrentOperationCount操作队列为1,如下:

class myOperation1 : Operation {
    override func main() {
        print("op1 working....")
    }
}

class myOperation2 : Operation {
    override func main() {
        print("op2 working....")
    }
}

let op1 = myOperation1()
let op2 = myOperation2()

op1.completionBlock = {
    print("op1 finished")
}

op2.completionBlock = {
    print("op2 finished")
}

op2.addDependency(op1)

let opsQue = OperationQueue()
// setting maxConcurrentOperationCount to 1
opsQue.maxConcurrentOperationCount = 1
opsQue.addOperation(op1)
opsQue.addOperation(op2)

3-将第一个操作加入队列后调用waitUntilAllOperationsAreFinished(),如下:

let opsQue = OperationQueue()
opsQue.addOperation(op1)
opsQue.waitUntilAllOperationsAreFinished()
opsQue.addOperation(op2)

顺便说一句,对于不复杂的任务,我更喜欢使用 GCD。

希望这会有所帮助。

【讨论】:

    【解决方案2】:

    完成块是在依赖操作开始后调用的,但这并不意味着第一个操作没有结束。

    正如@Xoronis 的回答中所引用的:

    无法保证完成块的确切执行上下文,但通常是辅助线程。因此,您不应使用此块来执行任何需要非常特定的执行上下文的工作。

    https://developer.apple.com/documentation/foundation/operation/1408085-completionblock

    看看这个例子:

    class myOperation1 : Operation {
        override func main() {
            print("op1 working....")
            for i in 1...10 {
                print("\(i)")
            }
        }
    }
    
    class myOperation2 : Operation {
        override func main() {
            print("op2 working....")
        }
    }
    
    let op1 = myOperation1()
    let op2 = myOperation2()
    
    op1.completionBlock = {
        print("op1 completed")
    }
    
    op2.completionBlock = {
        print("op2 completed")
    }
    
    op2.addDependency(op1)
    
    let opsQue = OperationQueue()
    opsQue.addOperations([op1, op2], waitUntilFinished: true)
    

    会导致

    op1 working....
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    op2 working....
    op1 completed
    op2 completed
    

    第一个操作确实在开始其依赖之前结束,但是在依赖已经开始之后调用完成块。

    【讨论】:

      【解决方案3】:

      根据完成块的文档(重点是我的),

      您的完成块的确切执行上下文不能保证,但通常是辅助线程。因此,您不应使用此块来执行任何需要非常特定的执行上下文的工作。 https://developer.apple.com/documentation/foundation/operation/1408085-completionblock

      因此,从更现实的意义上说,当您想确切地知道操作何时完成时,您应该执行以下操作:

      class myOperation1 : Operation {
          override func main() {
              print("op1 working....")
              //Do things
              print("op1 finished")
          }
      }
      
      class myOperation2 : Operation {
          override func main() {
              print("op2 working....")
              //Do things
              print("op2 finished")
          }
      }
      

      【讨论】:

        【解决方案4】:

        您需要将操作队列的 maxConcurrentOperationCount 设置为 1,才能按预期工作。

             let operationQueue = OperationQueue()
             operationqueue?.maxConcurrentOperationCount = 1
        
            let operation1 = Operation()
            let operation2 = Operation()
        
            operation1.completionBlock = {
                print("operation1 finished")
            }
        
            operation2.completionBlock = {
                print("operation2 finished")
            }
        
            operation2.addDependency(operation1)
        
            operationQueue.addOperation(operation1)
            operationQueue.addOperation(operation2)
        

        【讨论】:

          【解决方案5】:

          通过指定依赖,保证op2op1完成后被调度,但不一定op2op1的完成处理程序完成后被调度。

          【讨论】:

          • 我认为声明自相矛盾 - 接收者在其所有相关操作都完成执行之前不会被认为准备好执行 (developer.apple.com/reference/foundation/operation/…)
          • @BaSha:什么意思?你在哪里看到矛盾? addDependency 添加对操作的依赖,而不是对完成处理程序的依赖...
          【解决方案6】:

          初始化 operationQueue 挂起会给你你想要的。

          let queue = OperationQueue()
          let downloadOp = Operation()
          let resizeOp = Operation()
          downloadOp.dependency(resizeOp)
          queue.isSuspended = true
          queue.addOperation(downloadOp)
          queue.addOperation(resizeOp)
          queue.isSuspended = false
          

          【讨论】:

            【解决方案7】:

            我测试过使用 isSuspended。每次操作完成后才可以操作主队列。

            class OperationChain: Operation {
                var mainQ: OperationQueue?
                var text: String?
                
                init(with name: String, by mainqueue:OperationQueue){
                    self.text = name
                    self.mainQ = mainqueue
                }
                
                override func main() {
                    self.mainQ!.isSuspended = true
                    print(text!)
                    sleep(5)
                    self.mainQ!.isSuspended = false
                }
            }
            
            let oq = OperationQueue()
            oq.maxConcurrentOperationCount = 1
            
            let q1 = OperationChain(with: "Operation.main.q1", by: oq)
            print("q1")
            
            q1.completionBlock = {
                //sleep(5)
                q1.mainQ!.isSuspended = true
                var i = 0
                repeat {
                    i = i + 1
                } while i < 100
                print("q1.completionBlock") 
                q1.mainQ!.isSuspended = false
            }
            
            oq.addOperations([q1], waitUntilFinished: true)

            【讨论】:

              【解决方案8】:

              您可以使用适配器来确定执行的顺序:

              op1 working....
              op1 finished
              op2 working....
              op2 finished
              

              这里是修改后的代码:

              import Foundation
              
              class myOperation1 : Operation {
                  override func main() {
                      print("op1 working....")
                  }
              }
              
              class myOperation2 : Operation {
                  override func main() {
                      print("op2 working....")
                  }
              }
              
              let op1 = myOperation1()
              let op2 = myOperation2()
              
              op1.completionBlock = {
                  print("op1 finished")
              }
              
              op2.completionBlock = {
                  print("op2 finished")
              }
              
              let adapter = BlockOperation(block: {})
              
              adapter.addDependency(op1)
              op2.addDependency(adapter)
              
              let opsQue = OperationQueue()
              opsQue.addOperation(op1)
              opsQue.addOperation(op2)
              opsQue.addOperation(adapter)
              

              【讨论】:

                猜你喜欢
                • 2018-07-23
                • 2015-07-03
                • 2014-09-06
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多