I'd like to know whether my operation was cancelled or finished normally. Here is my code snippet :
class PacketReceiver {
private let m_executer = OperationQueue()
private var m_operation : Operation?
private func operationFinishedHandler()
{
if let op = m_operation {
if op.isCancelled {
print("Operation cancelled")
} else {
print("Operation finished OK \(op.isCancelled)")
}
}
}
func start()
{
let rxPacketOperation = PacketReceiverOperation()
rxPacketOperation.completionBlock = self.operationFinishedHandler
m_operation = rxPacketOperation
m_executer.addOperation(rxPacketOperation)
}
func cancel()
{
if let op = m_operation {
op.cancel()
}
}
func join()
{
if let op = m_operation {
print("is op cancelled = \(op.isCancelled)")
}
m_executer.waitUntilAllOperationsAreFinished()
if let op = m_operation {
print("is op cancelled = \(op.isCancelled)")
}
}
}
And later on I have :
let packetReceiver = PacketReceiver()
packetReceiver.start()
packetReceiver.cancel()
packetReceiver.join()
I see that inside join() method isCancelled property is set to true. But when I check it inside finished handler it is false. I guess it's an expected behaviour. But what is the correct way to check whether my operation was cancelled or not?
This is my operation class :
class PacketReceiverOperation : Operation {
private var m_packetData : String = ""
private let m_simDataProvider = SimDataProvider(okAttemptId : 2)
private let m_currentTimeProvider = CurrentTimeProvider()
private func prepareReceiver() {
m_packetData = ""
print("\(m_currentTimeProvider.getCurrentTimeAsString()) : prepare SIM receiver")
}
override func main() {
if isCancelled {
return
}
prepareReceiver()
if isCancelled {
return
}
var isPacketReceived = false
var isFirstChunkReceived = false
repeat {
if isCancelled {
return
}
if let chunkData = m_simDataProvider.getPacket() {
isFirstChunkReceived = true
m_packetData.append(";" + chunkData)
} else {
if isFirstChunkReceived {
isPacketReceived = true
}
}
} while isPacketReceived == false
}
}
For a variety of reasons (notably the lack of preemptive cancelation and general race conditions) the
isCancelledstate is not a reliable way of knowing whether yourOperationsubclass finished successfully or not, regardless. (It tells you whether you attempted cancelation, but not whether the underlying task succeeded or not.)For this reason, I rarely use the built in
completionHandler(with no parameters) and instead generally implement my own custom completion handler closure that supplies aResult(or whatever) as parameter. This eliminates all ambiguity.Or, in your case, you could make
isPacketReceiveda property of your operation, instead of a local variable, and then you could look at that to determine whether a packet was completely received or not rather thanisCancelled.You later said:
Yes, that’s true.
But it’s a bit worrisome that
joinis a synchronous method, at all, (i.e. that you’re callingwaitUntilAllOperationsAreFinished). The intent of operation queues is so we can adopt asynchronous patterns for potentially time consuming tasks and get this stuff off the main thread. Blocking while we wait for an operation to finish (especially if you’re callingjoinfrom the main thread) would seem to defeat that purpose.I’d encourage you to embrace the asynchronous pattern and just add your operation to a queue and initiate whatever you need in its completion handler (whether the default one or your own).