Serial queue, Concurrent queue, dispatch 0613
Serial queue and Concurrent queue
Queues can either be serial, which means the function that’s on the top of the queue gets pulled off, it runs to completion, then the next one gets pulled off. That’s serial queues.
Or they can be concurrent where the system pulls the top one off the queue, starts it running in a thread. If it’s got more resources, it takes the next one off, starts it running in another thread while the first one’s still running, and it might keep pulling a whole bunch of them off. So that’s a concurrent queue.
The most important queue, it’s a serial queue called the main queue.The main queue is where all UI activity has to happen. And this is super important for you to understand. That if you want to do anything where you call something a UI kit. UI button, UI anything, all right, with a couple of exceptions. We need be making these calls on the main queue. In other words, you can’t put those calls in a closure that you put on some other queue.
Now conversely, all non-UI activity that’s going to take any amount of time or resources, probably want to be off the main queue. So it never blocks the main queue. We want the main queue reserved for doing our UI staff as much as possible. Now this is not only cuz we want our main queue UI to be responsive, that’s the main reason, but also it serializes the activity on the main queue so that our UI is presented in an orderly fashion. If we allowed our UI to be on all these different queues, things would be drawing at all different rates and overlapping, it would be unpredictable as to what happened on the screen.
So we use the main queue as kind of a synchronization point where everybody comes back to draw on the main queue. Now the main queue can only pull a closure or a function off of the main queue and work on it when it’s quiet. In other words, it’s not off doing something else, like drawing or something like that. Now the system is using the main queue behind the scenes all the time.
For example, you know about draw rep. Draw rep gets called by the system and it doesn’t get called, you don’t call it, you don’t tell it, you just set needs display. You set needs display and as soon as the main queue gets quiet, it goes and runs some function that causes that draw rep to be called. So you see how that works. So that’s what is going on in the main queue. We say set Needs Display. It’s how we tell the system, please I need to be redrawn. And it will happen sometime in the future. Very close near future but sometime in the future. They will all get displayed at once.
How we put something on a queue — dispatch_async.
Dispatch_async takes two arguments.
- One is the queue to put it on, like the main queue or one of these other queues
- The second argument is the function to put on the queue, usually it’s a closure.
This is the closure you’re going to Put on there. What’s cool about these closures, they take no arguments, and then return no arguments. So they’re really easy to put into your code. And you can put anything you want in there. But if it is going to do UI, you’d better not put it on anything but the main queue.
So how do you get the queue? How do you get this little queue argument?
For the main queue, you call this function, dispatch_get_main_queue. You might ask why is all these underbars and why does this look like this? This is basically a CAPI, from iOS before swift, and so it comes into swift looking like a CAPI. These are swift, these are just Swift global functions. But the reason this is not object oriented, because this is a CAPI. This is called Grand Central Dispatch actually, that’s why they all start with dispatch, all this multithreading stuff, and that’s why it looks like this. The object-oriented way.
So all UI stuff have to be on this main queue. It’s a serial queue, things happen in order in the order they go in. And all time-consuming stuff, or even worse stuff that might block, like you’re going out to the network to get an image, like we’re doing where that block’s waiting for the web server on the other side to respond, all that wants to happen off the main queue. You still dispatch to that queue using dispatch_async. It says dispatch_async, not the main queue in here. And then open curly brace, so this is a closure, inside that closure you do the non-UI things that takes a long time or blocks. And then still inside this closure, you dipatch_aynnc again. This time back to the main queue to do the UI stuff that you need, you now put what you got in the UI.
So can you imagine this outer closure is off running on some other thread in some other queue and in the middle of it, it puts a block of code, the closure. It puts it back on the main queue, this one continues to run after that in fact dipatch_async, that function always returns immediately. Because all it does is put things on queues, it doesn’t actually execute any of the code inside the block. It just puts that block of code onto the queue. Then this outer closure just happily runs to completion and it’s done. Meanwhile, that inner block has been posted on the main queue and it just waiting for the main queue, for it to be first in line and for the main queue to be quiet, and then it’ll pick it up and run it.
So this is why we use the C function notation because it reads really cool. Dispatch off the queue this, then dispatch the main queue. It reads very clearly to the person who’s reading your code what your intend. It’s just being put on that queue, it’ll happen sometime in the queue when the queue is ready. When it rises to the top of that queue and the main queue is quiet.