iOS: Operations (NSOperations) — why again?

Mladen Despotovic
Swift2Go
Published in
4 min readJul 16, 2018

GCD or Operations

Photo by rawpixel on Unsplash

Every time I ask about the differences between these 2 approaches to queue tasks I’m getting similar responses:

  • use GCD in fire-and-forget
  • use GCD for simple, single asynchronous calls

but use Operations when

  • you want to cancel them
  • you want to execute more tasks in a one go
  • when you want to execute them in dependencies
  • when you want to prioritise their execution

These recommendations sound like GCD would lack some of the core features of Operations, yet it’s being said repeatedly Operations are more or less wrapper around GCD.

Well, the latter is certainly right and there seem to be nothing special with the Operations, what you couldn’t do with the GCD, actually, many things in an easier way, more readable, although the personal bias could play a role as it is with everything in our lives. I could say to the certain degree, that Operations did not fully achieve the task of simplyfing by encapsulation, because at least for my taste, too many things seem to be easier to implement just by using the GDC.

Misconceptions

However, I believe there are some real misconceptions about Operations which should be mentioned:

1. Cancelling the app doesn’t just happen. A developer must implement cancellation and setup KVC on a property to achieve that, OperationQueue just listens to it. Cancellation needs to be implemented same as you would do it in GCD as well. No magic here! If you need to cancel, for example, an asynchronous URLSession, you will have to use KVC to change the status to notify OperationQueue, but you will still have to implement cancelling the URLSession yourself. Actually, you have additional work with Operations here.

2. Group of tasks can be atomically executed easily with DispatchGroup as well, it’s not something that OperationQueues are capable alone, actually, it’s shorter, easier and leaner with the GCD.

3. Dependencies again, don’t just happen out of the box, when using OperationQueue. Operations need to be linked as dependent beforehand and all overhead of isFinished and the rest of the boilerplate needs to be implemented so that this mechanism work. I would argue, that using Dispatchon serial queue with GCD is more readable and easier to understand, especially if there are more consequent tasks in question.
However, if the tasks to be synchronised are themselves asynchronous by nature, then GCD becomes even much easier to use.

If there’s only one additional dependency, then we can nest the second task.

If there are more, then using DispatchGroups is a very easy thing to do…

There’s much more muscle to show, when dependencies could be between single taks and group of tasks and this is where GCD excels compared to Operations in terms of simplicity. Still, this is not the use case that would happen very often, so, not really a huge advantage.

Downsides

But here are some real downsides IMHO to using the Operations and I don’t want to repeat the overhead of boilerplate coding which I believe is the only thing most of the people mention when comparing the approaches. There are more important things to be mentioned here:

  • Operations are NSOperations and are bringing dynamic dispatch to all the subclasses. There is no Swift counterpart yet for it and there is a reason to believe there won’t be because of the next few things mentioned here
  • they use KVC and KVO, which are again unknown concepts in Swift and highly unlikely to be implemented anytime soon if ever.
  • we don’t have a stateless return of Operation result since the completionBlock of Operation is void. Consequently, we need to create a readable properties (state) on an Operation, just to pass back the result. Any kind of additional state inducts additional memory management complexity
  • Operations can lure developers to use them „uniformly“ even for the trivial tasks, wrapping simple code to “follow the approach”…
  • A bit more complexity in Unit tests, some helper OperationQueue mocks need to be made to be able to test it properly
  • Sometimes they just become a dump for all kinds of business logic, violating almost all SOLID principles. If anything, Operations should be mostly boilerplate with just 1+ method call and 1+ result property, IMHO.

Also when watching the WWDC 2015 video, I couldn’t find any key feature, where Operations would be irreplacable. Of course, they are a valid concept and sometimes a list of tasks, needed to be executed atomically can be nicely encapsulated in it, but in cases like that on the client, we should be asking if something is wrong with our API architecture itself and encapsulate complexity on the server side instead.

I created a very small repository and made comparison between 3 most common approaches of executing asynchronous tasks.

--

--