Threads and Grand Central Dispatch in Swift

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hi my name is Jordan Lynch and in this video we're going to take a look at threading and Grand Central Dispatch in Swift we'll investigate what threads and queues are as well as the Grand Central Dispatch or GCD the API for managing your queues in the process will also touch on synchronous versus asynchronous code and finish off with an overview of dispatch groups but this is something you want to learn then keep watching before I jump into the code I think it's best that we first get a better understanding of the beast most modern operating systems let you specify a thread of execution to use every iOS application has a main thread which is their to display your user interface and listen for events complex computations may slow down the main thread and freeze the app and here's where multi-threading comes into play we must move all of the heavy lifting to a background thread and then move the result back on to the main thread these threads might be executing their code simultaneously with a multi-core iOS device but they might also just be switching back and forth really quickly and some threads get a higher priority than others EQ is just a bunch of code blocks lined up or queued up waiting for a thread to execute them nope you don't need to worry about threads and Swift only cues the system takes care of providing and allocating threads to execute code off the Q's GCD stands for Grand Central Dispatch and this is the API for managing your Q's and its main responsibilities are creating EQ and putting blocks of code on a cube blocks of code waiting in a queue are held in the closure and we'll look at that shortly but first let's look at the different kinds of Q's there are two different types the main Q is the most important one of all it's the Q where all blocks of code that affect the UI must be run on the system uses a single thread to process all of the blocks of code from the main queue background cues are used to queue up any long-lived non-ui tasks these queues are often running simultaneously and in parallel with the main UI queue and you as a developer can determine which of the background cues get more priority over other ones and you do this by specifying a quality of service or QoS and we'll review that shortly to create a queue you use the dispatch queue struct in one of its static functions to create a queue on the main thread we use the static function mean to create a background queue you use the static function global and you can specify one of four different quality of services or priorities these two are equal ease of service that you apply when a user needs the task to complete promptly user interactive tasks have the highest priority of the system and use this class for tasks or queues that interact with the user or actively update your apps user interface for example use this class for animations or for tracking events interactively user initiated tasks are second only to user interactive tasks in their priority on the system you assign this class to tasks that provide immediate results for something the user is doing or that would prevent the user from using your app for example you might use this quality of service class to load the content of an email that you want to display to the user the next two types are lower in priority for execution utility tasks have a lower priority than user initiated and user interactive tasks but a higher priority event background tasks you assign this quality of service class to tasks that do not permit the user from continuing to use your app for example you might assign this class to long running tasks whose progress the user doesn't have to follow actively background tasks have the lowest priority of all tasks you assign this class to tasks or dispatched queues that you use to perform work while your app is running in the background like maintenance tasks or cleanup so the four priorities are listed here in the order of priority if you don't specify a QoS it will be say assigned the default case which is between user interactive and utility there are two ways that you can add code tears queue either synchronously or asynchronously when a work item is executed synchronously with the sync method a program waits until the execution finishes before the method returns that is before the next line of code following the code block gets executed this means that it will block the user from doing anything until the code is completed the code block is executed as the closure on either the main or background cube like this when a work item is executed asynchronously the async method the method call returns immediately and the execution of the closure is done the same way as above we just use async instead of sync both synchronous and asynchronous executions can be done on the main or background threads but the fact of the matter is you almost never use a synchronous execution so we'll focus on asynchronous but enough of the theory and let's take a look at it practice let's create a new playground and type in some code in our last two slides we talked about how with dispatched cues you can execute your code synchronously or asynchronously with synchronous execution the cue waits for the work and with asynchronous execution the code returns immediately without waiting for the task to complete let's create a global cue with asynchronous execution inside the closure will sleep for two seconds to simulate some lengthy process and then print a statement we'll follow that outside the closure with another print statement so print outside when we run this it takes a while for our print statements to show up it waits two seconds prints the first statement and then carries on after the closure has completed the sync code is blocking the execution at that second statement until after the closure has completed let's change sink to async and update our print statements when we run we get the second statement executed immediately and then two seconds later receive it first statement the async execution is not blocking our program from preceding there's one more async execution to consider if you want to delay the start of the code we can use async after and it can be executed on any cube but typically on the main here with async after you can delay the start by specifying a deadline and entering dispatch time now and then adding a number of seconds you this can be shortened like this and it's quite readable we just enter dot now plus two so with this code the first block will execute after five seconds and immediately goes on to the next block which only waits two seconds so running the code we get that second print statement after two seconds and that second block will have waited two seconds already so it only has three seconds more to get executed this is more complicated than you may think you'll not be calling dispatch Q dot global very often on your own and the reason is because a lot of asynchronous iOS api's do the tasks automatically on a background cue sometimes unbeknownst to you so let's take a look at another example for this I've created a starter project in Swift Eli and you can download it from the link in the notes below let's take a look at the code when the app launches the content view is presented it contains a space to display an image followed by two buttons the first button will call code that will retrieve a dog image and will update the state to refresh the screen with that image once it's been retrieved the second button simply toggles a state variable that will cause the screens background to alternate between green and white based on that boolean state value I'm using the mvvm architecture here to keep our code separate and in the dog view model there is our published image that when change will update the observed object in the content view causing the image to be displayed now I want to be clear here that this is not normally how I would structure this piece of code this is simply a way that I can demonstrate this concept of threading and gcd the get dog function sets the image to nil no image then transforms the string into a URL and grabs the content of the URL which happens to be a JSON object we use JSON decodable to decode the object into a dog instance and all I'm concerned with is the image URL once we have the image URL we can make a call to this URL extracting the image data then to simulate a slow connection I'll sleep two seconds before converting the data to a UI image and assigning it to the image variable which will then update our UI on our content view let's see how it works when I run it I see that I can toggle the background color at will but when I execute the fetch for the image our UI is blocked even though I'm tapping on the change background button the change doesn't happen until after the image has been retrieved and the two-second sleep has completed the reason is that by default our code is synchronous so it waits for however long it is going to take to fetch the json data and the image and the two-second sleep before going on to setting the image we can fix this then by putting all the code above this in an asynchronous background queue before we do that however I'm going to insert some print statements so that you can see what's going on to assist I have a couple of static utility functions the location function takes one parameter which is the name of the file that is executing the code and prints out the location and calling function the second cue function calls an extension on dispatch queue that accepts whatever string I want to give it and then prints out the action string followed by the type of queue you are on and the thread so back in our function will call that log dot location function passing in the special literal for the file just before we get our JSON let's log the queue information similarly just before we get the image let's log the queue information there too and finally let's log when we upgrade to our UI let's bring up our bottom panel and see what gets logged when we run our app and fetch an image we see that we're in the dog viewmodel file running the get dog function at line 15 and each of our three queue logs I see that we are on the main queue and thread so now let's finish our blocking problem what I'm going to do is place all of our execution code in a background queue I'm going to use dispatch Q dot global with a dot user interactive quality of service and execute this asynchronously let's run it I can tap the get dog image button and while it's fetching the image change the background that's great but not so fast the app hasn't crashed but I do get this notification that publishing changes from background threads is not allowed if we do not fix this there's no guarantee that our app will perform consistently we also see the offending line of code is highlighted with a purple notice telling us that this must be on the main thread well we know how to do this we'll surround this code with another dispatch cue but this time not mean and do it in synchronously as well this time as before we can change the background as the image is getting fetched and there are no warnings once the image has been retrieved our log shows that indeed the fetching of the JSON and the image were done on the user interactive background thread and the updating of the UI is done on the main thread as it must now everything comes at a cost and so does this simple approach this is a reason people tend to use another method for network requests which are not just for JSON strings and images Swift lets you observe the progress of the network operation in real time if you choose to use URL session and its delegates this comes in handy for a larger network request where you can the user know about the progress with fancy progress views or something else for simple image loading our easy approach will do more than good however for sophisticated tasks you better be looking at this different method so instead of using our own code as we have done here you'd normally use either URL session or something like alamo fire to fetch data like images from an external source first let's create a new function called get dog with session and I'll paste in the first four lines of code from above we'll also create another variable called session that is a shared instance of URL session we'll first check to make sure that the URL is valid next we'll call the session data task function passing in the URL and it has a completion Handler that when the task is completed provides the data a URL response and a possible error we're just going to ignore the response but let's lock that we are processing the fetching of this JSON now if an error has been returned we'll crash with a fatal error after that we'll do exactly the same as we did above within our background queue the final thing we have to do is ensure that we do have a task resume or else our code won't be executed back in content view let's make sure we call this function instead of get dog testing this we see that it runs just as well as the get dog function did in our console we see that even though we did not do a dispatch queue on to a background thread we actually are and its quality of services unspecified but I don't really care it just works the URL session handled the placing of our code onto a background thread and of course the UI is updated on the main thread because we left it at that there's one final topic I want to cover and that's dispatch groups groups allow you to aggregate a set of tasks and synchronize behaviors on the group you can attach multiple work items to a group and schedule them for a synchronous execution on the same queue or different queues when all work items finished executing the group executes its completion handler you can also wait synchronously for all tasks in the group to finish executing and the purpose of dispatch groups is to let you know when several independent tasks have completed so let's return to our playground and I'll comment out the previous code and I'm going to create a global dispatch queue in a dispatch group so let Kiel equals dispatch queued on global and let group equal dispatch group now there's two different ways that I mentioned to implement this and each way gives a different result in this first example we'll take a look at the synchronous waiting here's how we can wait for two tasks to complete we'll add a group parameter to our async execution I'll sleep for three seconds and then print task one is done let me just copy that code and paste that and I'll change it to sleep for one second and print task two is done now I'm going to follow that with a group dot wait and this specifies that everything must wait until all of our group tasks are completed before carrying on once completed I can print that all the tasks are done and we can continue execution when I run because our two tasks are asynchronous we see the print from tasks two first because there is only one second of sleep then two seconds later task one is done then only after both have been completed we get the final two print statements the second way for working with dispatch queues is by balancing the calls to enter and leave we indicate the start and end of an async operation and this is especially useful when working with code that you cannot change for example system or third-party frameworks we can keep the same queue in coop but let's comment out the code we'll start by entering the group with group enter now we can execute our async closure with the group parameter as before but at the end of the closure we'll leave the group with a group leave as above let's repeat this with a shorter sleep time now we can get notified when the groups queues are completed and execute some code and we'll use the group got notified function with the queue as an argument outside of this notification let's print continue execution now when we run this we get the continues execution printed immediately because the notify function is not blocking our code it's asynchronous this is then followed by the second asynchronous task because it had the least amount of time to wait and then the first task before finally printing all tasks are done so I hope this has given you a better understanding of threads and the synchronous and asynchronous nature of dispatch queues [Music] I have lots of other videos available and in the queue as well so please check out the rest of my channel you can also visit my website to see the apps that I have available on the App Store and visit my github page to see what I have available as public repositories if you like what you've seen give it a thumbs up and subscribe to my channel and ring the bell to get notified when I post new videos I'm most active on Twitter so please follow me there as well to find out what else I'm up to thanks for watching
Info
Channel: Stewart Lynch
Views: 7,656
Rating: undefined out of 5
Keywords: Thread, GCD, Grand Central Dispatch, DispatchGroup, swift
Id: uRLcV2Rvheg
Channel Id: undefined
Length: 22min 13sec (1333 seconds)
Published: Sun Jul 26 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.