Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why my NSOperation is not cancelling?

I have this code to add a NSOperation instance to a queue

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

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

When user leaves the view that triggered this above code I cancel operation doing

operationQueue.cancelAllOperations()

When testing cancelation, I'm 100% sure cancel is executing before async method returns so I expect operation.cancelled to be true. Unfortunately this is not happening and I'm not able to realize why

I'm executing cancellation on viewWillDisappear

EDIT

asyncMethod contains a network operation that runs in a different thread. That's why the callback is there: to handle network operation returns. The network operation is performed deep into the class hierarchy but I want to handle NSOperations at root level.

like image 948
StackOverflower Avatar asked Oct 29 '15 15:10

StackOverflower


2 Answers

Calling the cancel method of this object sets the value of this property to YES. Once canceled, an operation must move to the finished state.

Canceling an operation does not actively stop the receiver’s code from executing. An operation object is responsible for calling this method periodically and stopping itself if the method returns YES.

You should always check the value of this property before doing any work towards accomplishing the operation’s task, which typically means checking it at the beginning of your custom main method. It is possible for an operation to be cancelled before it begins executing or at any time while it is executing. Therefore, checking the value at the beginning of your main method (and periodically throughout that method) lets you exit as quickly as possible when an operation is cancelled.

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

try this simple example in playground.

if it is really important to run another asynchronous code, try this

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


        // etc
    }
})
like image 191
user3441734 Avatar answered Sep 20 '22 07:09

user3441734


it's because you doing work wrong. You cancel operation after it executed. Check this code, block executed in one background thread. Before execution start – operation cancel, remove first block from queue.

Swift 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!

Swift 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!
like image 42
dimpiax Avatar answered Sep 21 '22 07:09

dimpiax