Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding sequence of operations with dependency in Swift

Referring https://developer.apple.com/reference/foundation/operation, I am having Playground setup as -

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)

And console log is -

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

Shouldn't we expect output as result of dependency? -

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

Same result with using - opsQue.addOperations([op1, op2], waitUntilFinished: true)

op1 working....
op2 working....
op1 finished
op2 finished
like image 394
BaSha Avatar asked Feb 14 '17 09:02

BaSha


2 Answers

In fact, I can't determine what's exactly the mystery of why your code does not work as it should, but I figured out 3 workarounds to achieve what are you trying to:

If you are expecting that the output should always be:

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

then:

1- You might want to add the second operation to the queue in the completion block of the first one, as follows:

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- Setting maxConcurrentOperationCount operation queue to 1, as follows:

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- Calling waitUntilAllOperationsAreFinished() after adding the first operation to the queue, as follows:

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

btw, for a non-complex task, I prefer to use GCDs.

Hope this helped.

like image 160
Ahmad F Avatar answered Nov 15 '22 06:11

Ahmad F


The completion block is called after the dependancy operation starts, but it doesn't mean that the first operation didn't end.

As quoted in @Xoronis's answer:

The exact execution context for your completion block is not guaranteed but is typically a secondary thread. Therefore, you should not use this block to do any work that requires a very specific execution context.

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

Take a look at this example:

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)

will result in

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

The first operation does end before starting its dependancy, but the completion block is called after the dependancy already started.

like image 22
Aviv Ben Shabat Avatar answered Nov 15 '22 07:11

Aviv Ben Shabat