The Advanced NSOperations (https://developer.apple.com/videos/wwdc/2015/?id=226) talk from this year was a real eye opener. I've used NSOperations in my app before but not to the extent as shown in Advanced NSOperations.
I've spent a little time to translate some of the sample code from the Advanced NSOperations for my app and have started using the custom operations and queues to implement CloudKit syncing.
I also took some time to read the Concurrency Programming Guide as well as the docs for NSOperation and NSOperationQueue. As I did I made some notes about things in the docs that didn't seem to match up with the sample code implementation. I'm listing them here just in case they come to anyone else:
In Operation.swift, the isReady method is overwritten to support custom conditions. If the Operation is .Pending, and the super's isReady returns true, then the conditions are evaluated. If the state of the Operation is .Ready, then the operation isReady returns true if the super's isReady returns true. Here is the implementation:
override var ready: Bool {
switch state {
case .Pending:
if super.ready {
evaluateConditions()
}
return false
case .Ready:
return super.ready
default:
return false
}
}
In the NSOperation docs however, it states (in regards to overridding the isReady propery):
"In OS X v10.6 and later, if you cancel an operation while it is waiting on the completion of one or more dependent operations, those dependencies are thereafter ignored and the value of this property is updated to reflect that it is now ready to run. This behavior gives an operation queue the chance to flush cancelled operations out of its queue more quickly."
Based on this, it would seem that if the operation is in it's .Cancelled state, isReady should return true, thus allowing giving the queue the chance to flush this operation out of the queue.
Following along the early cancellation thread, in Operation.swift the `-start` method is overridden to look like this:
override final func start() {
assert(state == .Ready, "This operation must be performed on an operation queue.")
state = .Executing
for observer in observers {
observer.operationDidStart(self)
}
execute()
}
That assert above means that reaching this method in anything other than the .Ready state is a programmer error. But according to the NSOperation docs on -start, this isn't the expected behavior:
"The default implementation of this method updates the execution state of the operation and calls the receiver’s main method. This method also performs several checks to ensure that the operation can actually run. For example, if the receiver was cancelled or is already finished, this method simply returns without calling main. (In OS X v10.5, this method throws an exception if the operation is already finished.) If the operation is currently executing or is not ready to execute, this method throws an
NSInvalidArgumentException
exception. In OS X v10.5, this method catches and ignores any exceptions thrown by your main method automatically. In OS X v10.6 and later, exceptions are allowed to propagate beyond this method. You should never allow exceptions to propagate out of your main method."Should there be a check here to see if the state is .Cancelled and thus skip calling executing and just go right to .Finished or .Finishing?
That is all I have for now.
Thanks again to the team for this great talk and releasing this sample code to the world.