iOS Concurrency Live Tutorial Session with Audrey Tam - RWDevCon 2017

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey what's up everybody welcome to session 4 on Iowa's concurrency and in this session we'll look at some killer features of Apple's - concurrency technologies Grand Central Dispatch and operation queues so these are highlights from my video course so if you've already worked your way through that I'll completely understand if you want to go and learn about animations from Caroline or about get from Sam because I'm certainly going to watch their sessions when the vault comes out so if you stay GCD an operation queues help keep your apps user interface responsive by running the slow tasks off the main queue and they provide features that you're going to want to use with asynchronous api's like animations and networking so you'll see in the demos how to wrap these in a dispatch group or an operation queue now running tasks at the same time can cause problems and you'll see how to make your data objects thread safe to avoid race conditions and the standard way to do this is with dispatch barriers so that's why we're starting with GCD but first let's have a quick review of some terminology you can dispatch a task either synchronously or asynchronously onto either a serial or a concurrent queue synchronous versus asynchronous is about the source queue a synchronous dispatch blocks the sole source queue until the task finishes and a synchronous dispatch lets the source queue continue immediately serial versus concurrent is about the destination queue a serial queue runs one task at a time and any other tasks have to wait whereas a concurrent queue can run more than one task at a time Swift three syntax makes life so much easier I'm one of those people who used to always have to go and copy and paste when I wanted to dispatch onto the main queue but now it's much much easier there are five global queues one for each quality of service level ranging from high performance to high energy efficiency and to get the main queue is just dispatched queue domain and that's where you run your UI updates you can also create private queues and their serial by default if you want to get a concurrent queue you just use attributes concurrent and you add tasks to a queue with a sink or sink and you can put the task into a closure so typically you're going to dispatch Q dot global async you're gonna do something expensive that you want off your main thread and then when you want to come back to update your UI you just batch queue main async and do whatever it is that you need to do as the compulsory doke photograph so multiple dogs showing that multi-threading can be tricky to get right thank you so concurrency problems are hard to debug because they're unpredictable they don't run at the same time in the same order for the same duration every time you run them so they're intermittent problems so priority inversion can happen when a high priority task can be preempted by a low priority task for example you've got a low priority task into the queue locks a resource high priority task comes along once that resource and has to wait and that will slow down your app because this is a high priority task apple solution is to promote low priority tasks when a high priority task enters the queue and so the high priority tasks still has to wait but the lower ones get to go faster and they release their resources sooner the race condition arises when two threads access the same resource and the end result depends on how the threads are scheduled so if you're lucky task a reads and writes the resource before task B reads and writes the same resource and then everything is fine but another time you run it task B might read before task a writes and then task ve writes an incorrect final value so xcode's new threads and sanitizer tool can detect race conditions and will soon see how to make an object thread safe the third potential problem is deadlock this happens when two tasks are waiting for each other to release the resource task a holds resource X task B holds resource Y and each task is waiting for the other resource and so neither task can progress or finish so GCD and operations will help you avoid priority priority inversion and to some extent race conditions but you just have to keep your wits about you to detect and avoid deadlocks so some general advice is to help prevent priority inversion just use one quality of service level for any tasks that access the shared resource and you can avoid rece race conditions by using a serial queue to restrict access to a shared resource you always have to be careful using synchronous dispatch because it blocks the current queue and so if your task requires access to the current queue then your app will deadlock so you should never call sync on the current queue because the task won't be able to run and it will never finish and you should almost never call sync from the main queue it will block the UI the one exception is using sync to control access to an object's value it's a very short critical task using a sink to block the current queue while you're getting or setting the value will keep the value consistent thread safe code can be safely called from multiple threads without causing a race condition and the simplest solution is to use a serial cue to restrict access to the value so only one task at a time can read or write it but that's kind of restrictive so if you can't afford to do that then you can use dispatch barriers so what happens with a dispatch barrier is you got tasks that only read they can be running concurrently and then you have a task that changes the value and it gets into the queue and it comes in as a barrier task so any task that's already in the queue finishes but no other new tasks are allowed to come in that's what the barrier task is doing it's keeping new tasks from coming in and then when all the current tasks have finished and the barrier task runs as if it's in a serial cube runs all by itself and then when the barrier task finishes you can go back to being a concurrent queue new tasks can come in so we're going to see how to do that in the demo now the other cool GCD feature is this dispatch group if you want to know in all the green tasks are finished then you add them to a dispatch group and similarly you can do for the blue and the orange tasks and then you can be notified when all the tasks in a group are completed so this is a pretty cool feature and you're probably going to want to include a synchronous function calls in your dispatch group but asynchronous functions return immediately and this this batch group might think it's finished when it's still working so you have to adapt it so the group knows when it's really finished and that's the topic of the first exercise in demo one so how look let's see if this is actually working all right this is not quite sure to set this out what's going on here all right all right so this is your ray Wonderlic set of folders we're up here in for iOS concurrency and we're in demo one and we're in starter and we need this playground now before I really get into that down here the slides as well and then right at the bottom of course you already know that there's the PDF full of all of the tutorials and things so this is Caroline's actually so see that's fine okay and this is demo one now these documents here are just there in case you get out of sync with me and you need to go through and copy and paste some code to catch up that's what that's there for and by the way if you use the table of contents view then it's pretty easy to find stuff okay now why where's my Xcode okay that's our demo one okay now there are several pages in this so in case you're ready to find your way around more directly you can open up the Navigator and you can see that there are some pages in there as well okay so we still start on the first page and the idea of the first part this this demo comes in two parts the first part of the demo we're going to extend the animate with duration method so that we can add it to a dispatch group and if we do that then we can run multiple animations and then we can do something when all the animation is finished but first I want to show you how you might try and do this without just batch groups okay so these are the views we're going to use so the first view is just a Red Square it's going to be our background our main view and then we have a yellow smaller square that's going to move around inside of the Red Square and down here we have a label that's going to say all done and we want that to appear after all the animations have finished so initially it's hidden okay so here are some animations so in one second the box is going to move from the upper left corner of the main square down to the lower right corner and after it's done that it'll take three seconds to rotate through 45 degrees at the same time the main view is going to change color from red to blue taking five seconds so in its completion handler I unhide the label okay so this this combination up here is going to take four seconds the color change is going to take five seconds so I figure okay I'll put the hi label into its completion handler Oh who's that right so if you've never looked at views in playground before it's really easy you just said it's live view equal to view and then you open the assistant editor up here open the assistant editor make sure you're on make sure you're on time line here okay and then you cross your fingers because Xcode can be really really strange sometimes I have to restart my Mac so many times yeah so anyway see there's a little play button right down here and click that okay so that worked now the reason that worked of course was because I picked the longest-running animation to put the completion Halloran but suppose you wanted to switch that around and you wanted to do the color change in three seconds and the rotation in five want to the slower rotation if you wanted to do it this way you have to start moving the completion handlers around and making them nil and if you had lots more animations you'd have to sort of go through and do the arithmetic to figure out which one was longer and that's really not very elegant so this is a job for dispatch group all right so you can press the little stop actually no it's gonna do it yourself just click through to the next page step one beat and that stops okay let's get a little bit of space here right so here the same views and down below are the same animations but in between we need a dispatch group so let's go ahead and say that animation group equal and to get a dispatch group there are no options you just say dispatch group and the default initializer that's our dispatch group and now we just want to we just want to wrap the animate with duration method to give it this group dispatch group parameter in there so the rest of it is exactly the same as the usual animate with duration method and we've just tucked in this group parameter so when you're wrapping a method somewhere you just have to call the the usual method so I found that the easiest way to get the right completion is to type type with duration and it gets you the right thing and so you just passed through the same parameters so duration duration and the animations are animations and let me see and that's going to need a completion handler the completion handler gets a success object and we just pass that on to the completion that came in through the through the usual call and that's an optional so completion question mark success and so that's that's the original animate with duration that's being wrapped in our new function and now we just have to hook it up with the group what we want to do is we want to tell the group when is this animate with duration starting and when is it really ending and that's pretty easy really just before you call the animate with duration you tell the group you're entering you just tell the group group enter and then when in the completion handler you tell the group couth leave that's it so now the group will know when we actually finish the animation and when the animation finishes it will tick it off this list of tasks and when all the tasks are finished then it can do something for you if you ask it okay so now we just need to rearrange our animations we so that they're added to the group and so if you look back there it's just that the group parameter comes in just before the completion parameter so let's go down here and just before the completion parameter we're put in group Boop and our animation group the one that we created up above and a comma okay and then we can grab all of that copy it and paste it in before the other two completions and you notice that this last completion handler still has that label is hidden equals false I'll leave that there for a minute while we do the notifying because that's what we're going to put them to notify all right so when the group all the tasks are finished then you want it to do something so here's our animation group dot notify and when you run a notify method it wants to know where which queue it should do this on and this is a view so you want it to be the the main cube so dispatch queue distance Q dot main that's so much easier and then you want the work item the work item can be in a trailing closure and it's that thing that we did in the completion handler of the last the longest your animation so that's label dot is hidden heeding equals false okay so we run hiding the label and now that we've got it in the notify then we can take it away from this last completion Handler and set that to nil okay okay so just to recap we have the same views we have an animation group we extended the animate with duration method so that we could add it to a group and then we went ahead and put the group animation group parameter into all of the animate with duration calls down here and then we set up the animation group to do something when all of the tasks are finished and that thing is that it's going to unhide the label that says all done and then we took away the translation handler there and so we still set live view to view and we still have our assistant editor open and because it worked the first time it will probably work this time no not going to work oh do you is he working for you yeah that's very frustrating Oh usually if it works the first time it'll work the second time oh that's right maybe it needs to be up here no no it's just not gonna come up here anyway if it's working for you go ahead and change those numbers the three to a five and the five two or three and then run it again and you'll see that it still works it will the all done message will only appear after all the animations have completed evil Xcode alright let's continue on to thread safety so this has also multiple pages so the idea behind thread safety is you've got class and there's something there that's going to try and change values in the class and you want to be able to access the objects of this class concurrently with several tasks running concurrently and so any task that's going to change values you want that to be a barrier task so that like it stops new tasks from coming into the queue and then it runs by itself and when it finishes then more tasks can come in and run concurrently so this is our class the class is a person and it has the first name and the last name then it has a method called change name which is going to change the first name and change the last name and then it asked to see the name the name variable just concatenates the first name in the last name now this is an interesting this is an interesting method changed name so if you go in here and I think that's person yep so in the change name method it's got a random delay before it changes the first name and then a possibly longer a random delay before it changes the last name and this is structured to make sure that we get a race condition it's basically giving plenty of time for the there gonna be five concurrent tasks running and gives them plenty of time to interfere with each other so that you know you really want to see something right away okay so that's what we're going to do so that's the class and let's go back up to step nope that's the wrong one okay so this is a very basically very simple thing just to show you that it works when when we only call it once it works oops okay oh you can select the main editor to get rid of the assistant editor all right you running a bunch yeah okay so when we just asked to see the value in a playground it appears over here in the sidebar and you can see that Brian Biggles has appeared so let's go and see what happens if we have a concurrent cue and we're running five concurrent tasks all trying to change the name so as I said we need a concurrent cue so let's create one so that worker cute equal dispatch queue and we want the label and the label is reverse URL so calm render lick or put your own in and don't work her we're okay now that by itself is a serial queue and if you want it to be concurrent you can say comma attributes dot concurrent and that makes it a concurrent queue now I also want to dispatch group because when all five tasks are completed I want to see what the final name is so let's have a name change group and that's just dispatch group the default initializer right so now we still have the name changing person Alison Anderson we have now five names charlie cheesecake Delia Dingle all the way down to Gina Gregory and those are deliberately set up so that we can tell at a glance whether we're getting the right names or not okay so now in the for loop down here to do two or three in the for loop we have a little bit of sleep and then we call change name passing it the new first name and last name and then we print out the current name of the name change in person and this these three statements we want to put them in as a tasks to the worker Q so we have worker Q dot de-stink yeah I'll do it that way so and then you just wrap the three lines as a task to the worker cute now that doesn't do anything special we also want to add it to the groove so go back up next to the dot async and add in the parameter group name change group it's in Texas so much nicer business okay so that's that does what we want is to do these now they're going to be five tasks being added to the work queue asynchronously at the same time they're being added to the group so when all five tasks are done then we're going to get something which is our notify method down here so when everything is done we want name change group to notify us now in a playground the main queue doesn't work very well so I'm not going to use that here I'm going to use the dispatch Q dot global and oh don't like that and then okay we'll put the work item in as a trailing closure all I want to do here is print the final name print the final name backslash open name changing a person not name I see yeah happy okay and the other thing that we can do in a playground is tell it to finish so playground page dot current dot finish execution okay so just going back up here we have a worker queue we're going to put five tasks onto it concurrently we have a name change group there are all five tasks are added to that when all five tasks are finished then it's going to print the final day and finish execution okay yeah okay so let's see what happens if you run it now because these are print statements they appear in the debug area and got an eva if ever said which is correct but all the others are wrong okay they've all been interfered with by the other tasks sneaking in and changing one name or the other before they got a chance to print out their current name so that's a race condition right okay so let's see how to fix it okay so what we need to do is go back to our person class and modify it so it's thread safe and to make it thread safe as I said we want to be able to put in we want to make change name a barrier task and a barrier task is something that comes into a queue and stops everything else from coming into the queue so that means that our person our thread safe person has to have a cute right so let's give it a cute we'll call it isolation queue isolation queue and that looks very similar to the worker queue so it'll be a dispatch queue with a label and this time comm dr. ray renderer lick dot isolation and again it was going to be a concurrent cute okay the reason you want it to be a private cues because you don't want to be messing up your system global cues with this kind of activity because it does stop all new tasks from coming in so you don't want to interfere with your system cues and then you want it to be concurrent so that it can get lots of work done when there isn't a barrier task running okay so that's our isolation queue and we're going to use that to override the change name method so override func change name and so we want that to go on to the isolation cube asynchronously and what goes in there is the super not change name and again just passed through the parameters that came in first name and last name okay so that doesn't that's not a barrier task that's just just dispatching asynchronously on to the a isolation queue look how easy go back up to your async add the parameter flags : stop barrier and voila you have a barrier task how easy is that okay alright the last thing I want to do is override the the name variable because there can be some sneaking in before you can get hold of the name variable and so you need to call sync on the isolation queue in order to protect it to synchronize access to it so we'll do a watch override var name which is a string and inside of that we return isolation Q don't sink this time and inside of that we return a super dot name so just to remind you what's happening is that five change name barrier tasks so that group of code that we had before so this is code down here so this sleep name changing person person change name and print those three statements are going into the single task onto the worker queue and we've got five of those going whenever the change name task enters the queue then it's a barrier task nothing else can enter until it's finished because of that we know that by the it will finish all by itself it will have a perfectly valid name when it's finished okay but then after that comes the print current name of the name changing person dot name and that's running on the worker queue it's now going to call synchronously on to the isolation queue which basically stops everything on the worker queue until it comes back with a name so nothing else in the worker queue even if it's a change name barrier task or whatever it stops can't do anything it can't sneak in there and change the name okay so everything should be safe now all we need to do is here we're using a person and we just need to change that to a thread safe person and I know that your document says and the atoms but that was just oversight but you could leave it as Allison Anderson okay that's it - let's go ahead and run this and it's perfect okay so that's pretty good now let me show you how to use the thread sanitizer it's also really easy you know you can't run the thread sanitizer in the playground so we have to have a project the project doesn't do very much except repeat what we've done in here so you can go ahead and close that and let me go and find my thread sanitizer sorry all right why do we need the isolation queue to read the name it's because otherwise because the print statement is not a barrier task it can be running concurrently with other things and so something else could be sneaking in and and changing stuff and so we need to protect this is a common design pattern just you know to synchronize to call sync whenever you're fetching setting or fetching the the values of a of an object that can be accessed by multiple threads okay mm-hmm Oh pants don't knew what that was here we go sorry oh I have a question so what is the role of the sleep function in the for-loop like why did why do we put put it there like without that will be thread conditions well I think again that's just to give all the tasks a chance to get in and interfere with each other okay I'm just yeah okay so let's see all right okay okay so let me just show you quickly how to turn on your thread sanitizer because it takes a while to run so all you need to do is go up here make sure you're in a reasonable simulator and you want to edit the scheme and you want to be in the run section on the diagnostics tab and you see here there's a third sanitizer easy as that I'm sorry okay you got the scheme part so you need you editing your scheme let's undo that and back up so your scheme is here under where you choose your simulator and it's this edit scheme thing here let's see if I can that help okay so it's just the edit scheme thing under where you go to run your um okay and that pops open this box and you want to be is usually is in the run on that run tab already and yours might be over here someplace and you just need to get over to the diagnostics tab and up here there's a thread sanitizer let's see yeah okay and if you want it to pause after each issue you can check that box but we're not gonna do that today okay and then you just close it and let me get it running because it does take a little while alright so here in the view controller I just basically have the same code that we've been using in the playground so initially there's a change name race that's just using the person class and then change name safely which is commented out uses the thread safe person and so here we can see what's going on okay right at the very bottom you can see it's printed out a couple of things that are not very not quite right Oh actually Charlie cheesecake yes that's okay hmm all right anyway but in the meantime it has found all these issues okay and up here you can see it's got a little purple exclamation mark with some number your number might be different it's often gives a different number usually at least things and if you click on that then it opens up your issues navigator and they're all race conditions on change name either the first name or the last name and yeah we knew that already but thread sanitizer found all these for us okay so if you want to fix it we just comment out the change name race and we uncomment change name safely and then run it again and yes it's perfect again okay now the interesting thing is it tells you thread sanitizer debugger support is active so once you've got it running properly you're not confused by all these error messages appearing in your debug console your debug console you know has correct stuff in it it's very good to have this reminder that you've got the thread sanitizer turned on because it you know adds time and size to your build so you definitely want that reminder so you can go and turn it off okay so just go back up here you can stop it and go back into your edit scheme and turn it off okay because it that's the only warning it's going to give you if it's not finding issues then you don't know that your thread sanitizer is on except that it's told you okay and that's the end of demo one are there any questions so the question was let's break it again it's correct thought and also turn on the thread sanitizer and break so the the question was if we have all these issues what do we do with the information that we've got issues that's probably a case-by-case thing this one in in this case it comes in and it tells us you see you click on an issue and it takes you obviously to to the place where it's happening and not sure I think most of these are just on the first thing well okay this was complaining about the last name so this would tell you that you've got potential race conditions and they're all happening in the same method and probably just a flag to you that you need to create a barrier task out of this because it's it's making changes to your object and yeah this is possibly a simpler example than you might but but yeah if you have a class and there's anything any method that's going to be changing values in the class that's probably a flag already that you shouldn't make that a barrier task and just wrap it in an isolation queue like this okay so that's the end of demo one and pretty sure that we don't have time for a break so let's go straight into the second part okay so the second part we're gonna learn about operation Keys the operation Q is just the cocoa equivalent of a concurrent dispatch Q dispatch Q's always run first in first out operation Q's that you set up dependencies between tasks and that can change the order in which they run so you add tasks to operation Q's as operations quick if you want to quickly create an operation you can create a block operation you can add more blocks before you start it it's an object-oriented wrapper for the default global dispatch Q and the reason it's there it is if case you have an app that is using operation Q's and you don't want to mess it up by calling dispatch Q's then you can use block operations to basically get straight choose the default global dispatch cube normally you subclass operation and then you can create objects of this class and the subclass can have input and output properties and you can add helper functions there like a class and but you always override me to call the function that you're trying to run as an operation okay so you can run an operation on its own but most of the time the reason why you want to use operations is so that you can use operation queues so you normally add them to that and then you let the queue manage the ordering and the execution and operational queue doesn't have any options you just say use the default initializer like dispatch queue domain there is an operation Q dot main and that's where you can run your UI updates and you can add operations one at a time or an array of operations in the lab you're going to be creating three operations and you want with dependencies between them and so it's convenient to use the array form of add operations so that you can add them all at once right now dependencies suppose your app has an operation that loads a file from a network and another one that decompresses the file into an image and you can see there that the output of the data loader could feed directly into the decompressor like that and operation queues let you chain operations together exactly like that so you feed the output from one operation into the next you would specify that the decompressor has a dependency on the loader and so the queue scheduler the operation scheduler knows the decompressor operation is not ready until the loader has finished this is the killer feature of operation queues you can build complex that dependency graphs and then let the operation queue handle everything for you but is always a fact operation dependencies is somewhere you could create a deadlock you have to be careful you can create a dependency cycle that will prevent your operations from finishing now it's perfectly valid to have an operation in one queue depending on an operation in another queue and that makes it harder to spot cycles you just have to draw the pictures so for example here we have the top operation cube you've got a nice sequence of dependencies but the middle operation also depends on the middle operation in the bottom cube that's perfectly fine nope no problem so far there's a problem okay if the middle operation and the bottom cube depends on the last operation the top cube it's the cycle and there's no way that that can that can never complete and you've got deadlock okay so you just have to draw the pictures and look for the cycles and then somehow try to work around them okay so now we get to asynchronous operations because operation queues and dependencies give you such nice control over your apps operations you will want to use a synchronous API is like URL session in your dependency graphs an operation queue starts to the dependent operation when the operation is been waiting for finishes but as we know asynchronous functions return immediately and so there has to be a way for the operation queue to know that it really has finished and what the typical solution is to write an async operation subclass which manages its state and communicates with the operation super class through KVL okay it's a little bit of work but you only need to do it once and then keep using your asynchronous operations subclass now the boolean properties for an operation are is ready is executing is canceled and is finished and they represent the current state and initially they're all false and at some point is ready becomes true usually because something it depends on has finished when the operation is ready then the Q call start if it's an async operation it needs to set it state manually to execute because there's nothing else there that's going to tell the operation scheduler that it's still executing because then the asynchronous task goes off does something by itself it during the time that it's working the it has its state has to remain is executing and when it finishes then it has to come back and say is finished is true is executing is false so we can't do this directly because the operation state properties are read-only but they are kvo compliant and so we're going to be setting up a sting operation so that we trigger notifications kvo notifications and that's demo too so let me grab my okay and if you open up demo two it says start work in sources async operation not switch to that so over here open your navigator and you'll see that there's sources and somewhere in here which one is it I said in a sink operation not Swift it's just a skeleton and it's got a top part here to handle the state and then we're going to override all of the operations states down here now the difference between an async operation state property is a singular it's just one state property it's going to have three cases and the operations the superclass operations state properties there are four of them there's one for each each state okay so we're gonna gonna have one so this state enumeration so we need first of all to set up in an enumeration so we have public enum state which is a string type string and it's going to have three cases ready executing and finished so the operations state properties all have ease in front of them and I've dropped the ears so that we can tell the difference between the async operation state and the operation state they look different but for kvo we need to return a key path with these that looks just like the operations state properties so here we have a file private that's wrong sorry it's supposed to be in the enum stream keep going sometimes Xcode is like that yeah it was just having a hissy fit okay so file private VAR key path string return so we put the is back on and we grab the raw value ready executing finished come on but we need to capitalize it okay and so that will give us back the operations state properties is ready is executing is finished okay so we use this now to get our state property and we're going to let it default to ready and now to take care of the kvo notifications we're going to use the will set and did set observers so just start with the we'll set and in there we need to do the will change value for key things and so the first one is the new value dot keypad will change value for key okay we're missing something here will change value for key right yes stop doing autocomplete why don't you and so we need it for the new value and we need it for the current state so let's think about what's happening here if we are moving from is executing to finished well starting our stage is moving from executing to finished then the new value key path is is finished and the state key path is is executing and we're notifying the operation scheduler about those two values okay and then we need a similar one for did change value what are we doing they just copy the wheelset and just change will - did and instead of newvalue up here we want the old value okay so that's why we need to do for the state and then down here there's an extension we're going to override he's ready is executing and so forth okay so thank you override open bar because we need to access this so is ready here's a tight pool now this is the most complicated one because the readiness of our operation doesn't just depend on what we think as an async operation the readiness of an operation is mainly determined by whether the things that depends on have finished and that's managed by the superclass operation scheduler and so we need to check so we'll return super super dot is ready and also whether our current state whether the basic operation state is dot ready okay and then we do similar things with executing and finished so we want to override open bar is executing also a pool and this one is easy we just returned whether or not state equals equals dot executed same thing for finished over I'd opened a bar is finished also a bull and we return state equals equals dot finished right now I'm gonna do one more which we're not gonna actually need for today what's your problem we'll just wait I'm gonna do one more override open bar is asynchronous also a bull and this is the nice thing Cronus the subclass so we return true now the thing is this asynchronous property is only used when you want to run your operation not in an operation queue but standalone and then the UM the system needs to know whether it's asynchronous you're not ok anybody spot what's wrong here no just pretending all right so those are the boolean ones we have to do now we have to override two functions start and cancel okay and in start all we care about is if the operation has been cancelled we need to set the state to finish so this is all about managing the state manually if it's canceled we need to set the state finished if it's being started we need to keep its state executing okay so if is cancelled state equals dot finished and returned now how can it be canceled without us knowing it's very possible that the the app will say operation Q cancel all operations so at the operation Q level it's going to just cancel all its operations so it's going to come down from there and that would trigger this is canceled situation okay otherwise if it's not cancelled then the normal role of start is to call vain remember you override main to define an operation and so starts purpose is to call main but because this is an async operation we have to make sure that the state is executing that's about it oh no sorry one more you have to override the can't scoops open cancel and the first thing we need to do here is call super cancel make sure that the operation knows that it's being canceled and we need to set state equal finished okay that was a great deal of typing of a lot of code so let's go back and have a look so first of all we declared a state enumeration with three cases and we create the key pass by gluing is to the raw value capitalized and then we set up the kvo notifications for when the state is going to change to a new value and when the state has changed from an old value that's what it does because it's it's changing to a new value and it says will change value to new and it did change value from old it actually the flags and errors you leave it as new at that point yeah it was just the rather mysterious workings of kV oh okay and then we used our state to override the operation the superclass operations is ready is executing is finished and we override start the main thing we were concerned about in here is to make sure that we know when the state is finished and we know when the state is executing has to be managed manually sorry well because if we didn't do this the as far as the operation scheduler is concerned the async operation the async function returns immediately and it looks like it's finished and so that's why we have to keep sort of override what do you think's and keep telling that no no is I'm still executing and what because you're telling it I'm executing it's your responsibility to also say when you're finished and we'll see that on the next page in fact okay so then it says now go to the playground page which is up here and we can close navigator so that was a lot of typing a lot of code would be good to get a chance to check it and also to see what the process is that you need to follow in order to to use all of this infrastructure so what we have here is a toy function a toy operation and it's a slow ad which takes two numbers and adds them together it's slow because there's this sleep in here and then I created an async version of slow ad it's asynchronous because it's got an operation queue that it gets put on to so here's our pretend asynchronous function that we want to now wrap in an async operation so we have a slow ad operation it has two inputs left-hand side and right-hand side and it has an output result and its main typically you know main you just call the function that you you want to run as an operation and pass through the inputs it gets back an output which you then store into the operations output and this is to do here that we're gonna come back - okay so it's tortoises all the way down we had operation we subclassed it into an async operation we subclass the async operation into a slow add operation these are still very generic things we need to instantiate this flow add operation by giving it specific inputs okay so the left-hand side 42 right-hand side 24 adds up to 66 so now we have a slow add operation and we create an operation queue very simple and we add operations and it's an array containing only slow add on because this is a playground I can say wait until finish true if you're doing it in your own app you want to be very careful because that blocks the current queue okay but in a playground it's pretty much the only way to to get what to where we want to be which is I want to wait until the operation was finished and then I want to show the result and then I want to finish execution so oops so this is going to work I would expect to see 66 over here in the sidebar and the run button should turn sorry the stop button should turned back into a run button down here see so you can see it's doing something but there's no 66 and this button is still running okay and that is because we didn't tell it we were finished so finish that and then go back up to the Judoon this is another closure so you have to say self self dot state equals dot finished okay so it's the responsibility of the main function in its closure after it's called the asynchronous function in the closure set the state to finished okay and now if I run it again 66 okay so that's just the lesson that we have to remember to put dot finished in okay let's go look at a more realistic operation this is an operation that's going to download an image from the internet and all it needs is it needs a image name which is a string and a completion handler to handle the image that comes back and it's got an output image as well as well okay look just open the navigator again and I'll show you it's down here in network simulator so the method the async method is download image takes a string the completion Handler and it's basically a URL session data tasks okay but because we're in a situation where we're not quite sure what our internet access is it's actually a mock URL session and all this code up here is creating the response to the data to create the session and then when I call a data stack upon the session it gives me back the response of the data that I fed to it so that's why it's kind of pretend but it still acts like a it still acts like a asynchronous function so that's important now while we're here let me show you what's in resources so there's some photographs here with train stations street lights and stuff like that we're gonna be using this one down here cuz it's pretty and now go back to the image load operation page okay so here's most of the image load operation it's got the image name the completion the output image and the initializer and we just need to override the main function and as I said before all that is is you want to call the method that you want to run as an operation so that's download download image named and when we create the operation we're going to pass in an image name so that's what goes in here that's the input image name and then we're going to have a completion handler for this and we'll just do that as a trailing closure it expects to get an image and the first thing you should do is save that into your output self dot output image equals image and remember really important thing for an asynchronous operation is to set it state self dot state equals dot finished and then we pass it on to the completion Handler that came in as the input self dot completion of image meeting oh right you have to be careful about this autocomplete stuff I was trying to that is actually correct no maybe so I was trying to stick the image name in there make sure you've got self-taught output image equals image not image name okay all right so that's our image load operation and that's the image operation class and to use it we need to create an instance of it that image load equal image load operation and the image name is train underscore night dot jpg and again we're going to make the completion handler as a trailing closure you know completion handler is going to get back an image it's an image in accept all I really want to do because it's a playground as I'm gonna all I'm gonna do is print the message image loaded in your own in an app you would probably want to dispatch asynchronously back onto the main queue so that you can display this image in an image view or you might want to just add it to an array or something like that but so you would do that so I'd actually you don't need that image I can put underscore in up there okay what's matter with you should be alright okay so that's our image load operation and then we need an operation cube and we're going to use the add operations do give his load in the in the array wait until we finished right and then after it's finished then I want you to have a look at that image and yeah you know we just want to playground to finish am i missing a bias but higher than that because indentation is wrong uh-huh okay so that was the main yeah okay all right right so all it's done over here or I asked to see the image it's given us the dimensions of an image and you can there's a quick look button here and there's another button that will show it in line and you can select that too late all righty all right supposed to grow anyway so yeah you can do that there okay and that's how you do that and unfortunately I think we're running out of time that the lab is worth doing because it has a lot of details about how you set up the connection between your operations so that you can pass the output of one into the input of the other one so are there any questions about what we've done so far the the is asynchronous property yes the is asynchronous property is only used when you run the operation not in an operation queue because when you run it in an operation queues automatically I'm not joining somewhere else whereas if you if you if you run an operation all by itself from the main queue it will run on the main queue and if it's asynchronous you need to flag that so that the operation scheduler knows to move it off the main queue but you did only work you only needed if you're not running it in an operation queue in the implementation of our maintenance do we have to set the state at the beginning of that to executing or is that managed somewhere so you know if you remember when we did the a sting operation definition we overrode start and in there when when we call main we set state to executing so that's taken care of and that's why we just have to make sure that we tell the scheduler that we're finished anything else okay akun hi well yes as I said it's really worth doing the lab at some point and I will be in bail after lunch across the hall so if you want to come and talk about that then that'd be fine so let's go on to what we learned so we started out with some terminology and some syntax and had a quick look at potential concurrency problems and how to avoid them and we had a fun thing going with UI view animate with duration and we added those animations to dispatch group so that when they were all finished we could do something like show a message and we also use the dispatch barrier to make the class thread safe and we saw how to turn on Xcode thread sanitizer and it would find race conditions for us in demo 2 we created an async operation class and implemented an image load operation and added it to the operation queue to run and in the lab if you get around to doing it there are two image filter operations with dependencies between them and they're going to chain the image load and then the tilt shift and then the vignette operation feeding the output of one operation into the input of the next one and there's a cute little protocol that you use to do that so where to go from here well as I said this presentation is highlights from our 12 part video course starring me and our website has a two-part GCD tutorial that was recently updated by Christine Abernathy and it covers basics thread safety with dispatch barriers dispatch groups asynchronous testing and lots more the final word on GCD and operations is Apple's concurrency programming guide and you can find me on twitter as matahari map I'm happy to help with any questions you have during lunch in a few minutes or in the Vale room after lunch or in fact any time during the rest of our w DEFCON so I hope you enjoyed this session thanks for coming and I'll see you around [Applause]
Info
Channel: raywenderlich.com
Views: 7,099
Rating: 4.8873239 out of 5
Keywords: concurrency, ios concurrency, ios swift concurrency, concurrency workshop, ios concuurrency workshop, ios concurrency for swift, swift operations, swift gcd, grand central dispatch tutorial, rwdevcon, rwdevcon videos, live tutorial, concurrency live tutorial, ios deadlock, ios race conditions, ios threading tutorial, swift threading, swift ios threading, live coding, swift live coding
Id: uxOrFvz0RXw
Channel Id: undefined
Length: 84min 25sec (5065 seconds)
Published: Fri Dec 08 2017
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.