Memory 4 - Fixing Common Memory Problems: Reusable Popups (iOS, Xcode 9, Swift 4)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hi everyone my name is Mark Boykins from today I'm at the Bonneville Salt Flats in Utah and I'm enjoying a beautiful day and all this around meat is actually salt alright in this video we'll be going over some common memory problems in iOS apps we're specifically looking at memory problems that can be created with the Notification Center callbacks and delegates if you're uncertain what these topics mean then watch my series on reusable pop ups I've cover all three of these topics and separate videos and make them very easy to understand this video will be using advanced methods of fixing memory problems if you haven't seen Swift memory mastery series part 1 2 & 3 yet then this content will be a little confusing because we're building off of what it's taught in those previous videos so if you haven't seen them then please watch those first so you're not overwhelmed or confused by this video because we're basically taking everything we learned in those three videos and we're applying it to the reusable pop-up project so watch those videos they will guide you step by step through important and advanced topics to make you a master in Swift memory in this video we'll be looking at a project that was built as part of another series where I taught you how to create reusable pop-up for your app that can be used from anywhere we covered three different ways on passing data back from the pop-up to the view controller that opened it we will take what we learned from the previous memory videos and apply it to this project if you're interested in watching that series on reusable pop-ups then I included a link in the upper right hand corner of this video just look for the I icon with a circle around it and then click on that there also be a link in the video description to this video we'll have three parts we'll be exploring potential memory problems concerning the Notification Center callbacks and delegates and again if you're not familiar with any of these concepts or these topics then I suggest you watch the series on reasonable pop-ups because that series really goes into depth on what these three things are and how to implement them all right let's jump into Xcode and start exploring potential memory problems with the Notification Center okay we're in the reusable popups project which was created from a series of videos on reasonable popups and we're going to cover memory problems with the Notification Center so this is where we're going to start but this isn't actually where I'm going to be checking for the memory problems instead what I had to do is create another view controller which is right here so I'm going to click on this button right here navigate away and I'm going to navigate to another view controller because what we want to test is to make sure that if your controller is completely unloaded from memory and you can't test that with this project right here the way it's set up because as you can see it uses a tab bar controller and what happens with the tab bar controller is when you click on these buttons down at the bottom on the different tabs it loads the view controller but it never unloads it because that's just how it works so it always keeps the view controllers in memory so you can't really test it being unloaded from memory because because iOS will never unload it from the memory so what I have to do is navigate away from it and open up a brand new view controller which is going to be this one right here and as you can see I made a different color just so it really stands out and you can see that we're actually in a different view controller and when I dismiss this view controller by clicking this button right here it should actually get unloaded from memory and that's what I want to test with the Notification Center I want to make sure when I use the Notification Center that I'm not doing anything that will keep this view controller in memory or the popups when I select date and the pop-up comes up I don't want that to be stuck in memory as well so how do we test them how do we know when a view controller actually gets unloaded from memory well the easiest way I found how to do that is to add this D in it to my view controllers D in it gets called right before something is about to be removed from memory so if this prints out right here then I will note that Notification Center was de initialized and it was removed from memory same with the pop-up so if we go into the pop-up view controller I also added that to the bottom right here so I know when the pop-up gets removed from memory and that's what I'm looking for this will get printed out in the console down below right here ok good so what I'm going to do right now is I'm going to run the project and let's make sure they pop up is removed from memory and this view controller with the Notification Center is also removed from memory I'm just going to move this over here so we can see this console output here and I'm going to navigate away because this view controller now that it's being shown cannot be removed for memory okay so I select a date now you may notice down here the date pop up view controller was the initialized so that means it was removed from memory that's that's good it's not being stuck in memory and then when I dismiss this view controller nothing showed up so we do have a memory leak here so we know that this view controller is not being unloaded from memory so let's go into the code and take a look at what's happening okay we're looking at the notification view controller and let me just walk you through this so we set up our observer and this is going to hold a reference to our Notification Center observer that gets created and then we have a closure here and as you know from the memory videos a closure is a reference type in itself it's its own object that is stored in memory so inside this closure we use the parameter to get a reference to our date pop up and then we take that reference and we access the formatted date and we sign it to a label that is on our notification view controller so this self right here is a reference back to this view controller so here we have a retain cycle and it basically starts with this notification view controller which has a reference to this observer because it's right here and this observer has a reference to this closure and this closure has a reference back to the Notification Center so let's take a look at this visually and see what it looks like okay if we create a memory graph or a diagram what the chain of dependencies looks like we would come up with something like this and as we can see we have a retain cycle inside of our view controller that's why it's not in loading in order to break this cycle we need to change at least one of these strong references to weak or unknown or we need to somehow remove it from memory annually we have a couple of options let's get back into the code and take a look at the first option okay the first thing I'm going to do is I'm going to take this observer and I'm going to make this week and that is one way to break this chain of references or this retain cycle or the circular reference because the basic problem is the notification view controller has a reference to observer an observer indirectly has a reference back to the notification view controller so they both references each other so they both have a retain count of one or it's also called a reference count because it's automatic reference counting one of those the notification view controller and this observer through the closure has a retain count or a reference count of one they can't be removed from memory all right so let's run this now and see if it works okay we select the date and that's fine that gets removed and then I click dismiss all right good so we notice that gets removed as well okay let's see visually how we fix this so what we did is we made the reference to the observer week and that breaks the retain cycle let's look at our second option where we can use a caption list so I'm going to remove that week and from our Swift memory mastery series we learned about capture lists and what those are so we're going to add a caption list here and as you know it's just with two brackets just like this and it's for closures so what I'm going to do is I can make this reference to self I can make it weaker unknown and if you remember from those videos you use week if there's a possibility that this self can be nil or you use unknown if you know for certain that it's not going to be nil and it'll always be available so I'm pretty certain that it's always going to be available so I'm just going to make it uh known like that now if I wanted to I could give it a different name I could say VC equals self so it's kind of like declaring a variable right and then what I could do is I could replace this self with VC and that'll work just fine like that but I can use the shorthand version too and I can just make it self okay great so let's test that and make sure that works okay well select a date first okay it's run through our closure let's run that code and it's populated this date so now if I dismiss it also gets Dee initialized okay good so that's working so here's what we did in this case down at the bottom we made the reference to self and unknown reference so that also breaks the retained cycle okay good now that we handled the memory leak there's one more potential problem that can happen which is specific to Notification centers and I just want to make you aware of it so you don't make this mistake in the future and it has to do with not removing the notification centers observer so let me show you how that happens now in our project we actually did take care of this by removing it on the view did disappear but what I'm going to do is I want to comment this out now a lot of times I've seen people and I've done this myself to where I've created an observer and I run some code but I never actually removed it from memory and here's the problem with it so let's run the project and see what happens okay when I click navigate away it runs through my view did appear right and it added a notification observer so when I click on a date it receives the notification populates my label that's fine I click dismiss it removes the notification view controller from memory so everything looks fine right but what happens is every time I come back in here it's going through this view did appear and it's adding an observer to the Notification Center so watch what happens when I set a breakpoint here I select date it stops on my breakpoint let me run it it stops again and actually this time it throws an error it says my unknown reference what has already been de-allocated but why did it and it runs again and it says the same thing on reference has already been D allocated so why is it doing this why is it coming through and running this three times it's because I'm not removing the observer and the Notification Center now notice we don't have to instantiate it so basically the Notification Center default is a singleton which basically means there's always one object in memory I have to instantiate it you don't have to make many references to it there's only one so you can't remove a Notification Center from memory because it's always there and I'm adding observers to it so even though I'm leaving this view controller the view controller gets D initialized but the Notification Center is always there let's see what's happening here when you use the add observer function it adds that information to the notification centers dispatch table dispatch basically means to send off a message or send a message off to someone in this example we see duplicate notification observers that we're adding to the dispatch table so every time the view did appear happen I'm adding a new entry to this dispatch table the Notification Center exists statically in your ask memory or as a singleton and like I said before we didn't have to instantiate a notification center right you can just use it because it's a singleton and it's always in memory it's much like the UI view dot animate function which is also a singleton that means it isn't connected to a view controller so if the view controller unloads the Notification Center will still exist along with all the observers that you headed to the dispatch table so remember hilary's move your observers from the dispatch table with the remove observer function so let's go back in here we can remove this breakpoint and we're going to remove this comment so we remove the observer okay that's it with memory problems would note Gaugin centers now let's move forward with callbacks next part should be fairly quick let's jump into the code and look at the potential problems we have with callbacks now if we look at the code let's just walk through what's happening here when I click on the button to show the pop up it grabs a reference to the pop-up right here with the pop-up variable and I set this closure property right here to this closure right here this is my callback Don save is a callback and then I have a reference to self this view controller so this kind of looks like a memory leak as it is right now right because you have the view controller and there exists a reference to a pop-up and that pop-up has a reference to self so it goes back around but it's not quite a memory leak right now because what happens is this callback view controller doesn't actually have a reference to pop-up well it does break here this is its reference but as soon as you go out of scope soon as soon as it leaves this if block and it leaves this function then pop-up gets removed from memory that reference gets removed from memory the pop-up is still in memory but this reference gets removed so when this reference is removed from memory the reference count will go down by one here's how we do create a memory leak and that is if I create a variable I call the pop-up and it was a date pop up view controller then that will create a memory because this will hold on to a reference to the pop-up controller even after it goes out of the scope of this function so this will always be in memory so let's test it now and see if it gets removed from memory okay I'm going to open up the pop-up save the date so we know the this closure ran this on safe closure ran or this callback and if I dismiss it we noticed that not only was the pop-up not unloaded but the view controller this callback view controller also was now unloaded or D initialized so we have a problem we have two things that are now in memory and they both have a reference count of one because they're both referring to each other so let's take a look at the memory graph so we can visually see what's happening here we have our callback viewcontroller has a strong reference to the pop-up variable and that's the one we just created and that pop-up variable has a strong reference to the closure to on save is a closure closures are their own objects in memory and then inside that on save we have a strong reference back to the callback view controller so there's our retain cycle right there again what we'll have to do is is break this retain cycle by changing one of these strong references to weak or unknown or we can manually set one of these objects to nil to reduce the reference count and I'll show you how we're going to do that in Xcode okay the most obvious thing that I'm going to do right now is just make the pop-up a weak reference so let's test that make sure it works select a date now we should see this pop up Denisha lies and it does okay good and I'm going to dismiss this view controller and we see that also gets the initializer de-allocated good so that works now I'm going to change that back and can you guys think of the second thing that we can also do to break this retain cycle yeah we're going to use a capture list here and I'm going to use unknown self and remember we can also use week as well that'll work as well and just to show you how that works is if I do change this to week there's something else I'm gonna have to change in my code because this self now is optional so it can be nil so I'll just have to add a question mark and that's the that's the difference between weak and unknown it is if it's weak it can be nil if it's unknown then it means I definitely always have a value there available when this code runs all right good so let's test that make sure that works select the date that should get de-allocated oh yeah this one's a little bit different so notice the pop up didn't get d allocated it's still in memory right now but if I click dismiss then they both get D allocated at the same time and that's because the view controller if we look at the code the view controller here has a strong reference to the pop up so it's going to hold on to that reference as long as this is in memory it's gonna keep this in memory I don't know if you guys remember that about strong references it basically means a variable will always be in memory as long as the parent is in memory so when the parent or this view controller gets removed all of its children get removed as well so the third solution that you can do is you can also set this variable to nil and when I set it to nil it's going to reduce the reference count by one so by manually reducing that reference count by one it breaks that reference cycle or that retain cycle so let's test that okay the pop-up was Denisha lies and then the viewcontroller was D initialized or D allocated alright awesome so everything is working perfectly those are three options that you have right there so let's review our solutions and see how they fixed our retain cycle our first solution was simply to make the pop-up variable week our second solution was to use a capture list and change the strength of self to unknown and we could have used week two and finally our third solution was to set the pop-up to nil after we were done using it in this way we manually lowered the reference count by 1 so it could be removed from memory and therefore breaking the retained cycle and I just want to mention here too that this is what is actually happening when we don't have a class level variable as pop-up as soon as it goes out of the scope it leaves the function and the reference count goes down by one if you can think of other solutions please feel free to offer them up in the comments below okay and on to our last part we're going to be talking about potential memory problems with delegates so let's take a look at delegates and what possible problems we might have with them okay so here's the delegate viewcontroller and as you can see I have a DNN coded here so we can tell when it gets removed from memory and this one works a little bit differently because I'm not using a segue instead I'm using a button and when I click the button it's going to find the storyboard and then from the storyboard it's going to find the view controller and then it's going to set the delegate to self and as you can see down here I conform to this protocol pop up delegate a handle this Papa values selected so basically the pop up is gonna call this function and I'm gonna supply it the code all right so let's test this now just to make sure where we're at is it working is it not working hey click on save time that looks like it's working it's getting removed from memory and I dismiss this view controller and that looks like it's working too okay good so right now it's in a working state so let's explore the possible problems that we can have okay now as he saw before we can create a memory leak if I create a variable local variable here for the pop-up all right and since I made optional I gotta include some question marks here and I have to ensure that it actually exists when I pass it into this present function this is what actually shows the pop up okay let's run it and see if that causes any problems and as I think you can imagine by now that will create a retain cycle as you can see the pop-up was not removed from memory and the view controller was also not removed from memory okay so by this time I think you guys know why we have a retain cycle let's just go over it again so you guys have a clear understanding on what's happening here okay here's what I retain cycle looks like when we're using the delegate and as you can see by now with these graphs that I draw out I always start with the view controller because this is what I believe is the parent of the two classes in the retained cycle so we have the delegate view controller and the date pop up view controller that are holding on to each other and if you remember from the Swift memory mastery video there are two steps recommended for fixing the retain cycle the first one is identify who the child is and in this case it's our pop up our pop up view controller is our child and I'm making the view controller the parent and then the second step is to make the child have a weak reference or a known reference back to the parent so in this graph where is the reference back to the parent the delegate property the delegate property holds a reference to the delegate view controller and we can confirm this by going back in the Xcode and looking at the memory address let's take a look real quick so you know what I mean okay I'm going to go on to the date pop up view controller and we can stop this for now and I'll just set a breakpoint right here and this is in the viewdidload so when the pop-up shows up we're going to take a look at the memory of this delegate right here actually there's one more breakpoint I want to set and yeah let's put it right in this right when we click the button so navigate away select time I'll hit the breakpoint and the self for this delegate viewcontroller look at this memory address right here so it ends with a five nine zero zero all right and if we take a look at the delegate here so the delegate has a reference to that view controller and you can see it right here a 5 9 0 0 so it's delegate property and our delegate view controller have the same memory address and that's why this pop up has a reference back to our view controller is through this delegate property so if we want to make the reference back to the parent week then what we want to do is make the delegate property in our pop up week so let's do that now and see how this works I'm just going to remove these breakpoints ok in the date pop up view controller what we want to do is we want to make this delegate week but let's look what happens here ok we get an error message week may only be applied to class and class bound protocol types not the pop-up delegate okay so so what does this mean why can't I make this delegate week this is a pretty common pattern and you pretty much see this used everywhere where there's delegates you always make the delegate property week so why is it giving me an error and saying that it can only be applied to class and class bound protocol types well in order to understand that error message we need to know more about protocols and apple says the protocol can be adopted by a class structure or enumeration to provide an actual implementation of those requirements the important part here to understand is that protocols can be applied to value types as well as reference types and currently the way our protocol is declared Swift thinks our protocol is being adopted by a value type and you can't apply week to value types so we need to bind or restrict our protocol to a reference type so Swift knows it's a reference type so how do we do that here's how we are currently declaring our protocol so what we need to do is make our protocol bound to just classes or restrict out this protocol to just classes Apple tells us exactly how to do that in their Swift documentation apple says you can limit protocol adoption to class types and not structures or enumerations value types by adding the any object protocol to protocols inheritance lists okay so that's exactly what we want we want to limit adoption of our protocol to just class types so Swift knows it's a reference type that Swift knows it's a reference type we can then make it week so our protocol will inherit from any object like this so let's go back into exco to make that change to our project and since I'm here what I'm going to do is hold down command and I'm going to right-click on pop up delegate and I want to restrict this protocol to only be used by classes to make them reference types and I'm going to use any object okay so let's navigate back to our pop up I'm just going to hold down command control and then left arrow to navigate back and let's build this and see if that error message goes away okay we rebuilt it and the error goes away so I could explain what just happened here the first time I built it I still get that error message so I think that was an error in Xcode because it actually wasn't a problem it wasn't until I actually ran the project that their message completely went away so that did fix it by adding the any object to our protocol we're now able to make it weak so let's run it and make sure that everything is unloading from memory correctly look at that I see the error message still comes back up which is wrong I don't know why Xcode is doing that maybe it's a problem with Xcode 9.2 that's the version I'm using okay so let's go to delegate and we're going to navigate away we're going to select a time and notice the public doesn't get removed at this point but when I dismiss the view controller they both get removed from memory so I don't know why this error is still showing up even though it's not in there maybe what I have to do is clean the project I'm not sure now I still keeps coming up okay this might be a bug in Xcode but as you saw it does still work okay so let's review what we just did here's where our graph looks like now we've broken betaine cycle by making our delegate have a weak reference back to the class that is assigned to it the parent like I said before this is a pretty standard practice and it's a good practice to keep in mind and follow when working with delegates now of course you do have other options making the delegate week is the best one in this case you could have also assigned nil to either the delegate or the pop up property once you are done using them or you can also make the pop up week like we did previously so let's explore some of those options okay we're going to go back to our delegate view controller and actually what I'm going to do let's go back to the delegate and let's remove the week and then we'll go back to our delegate view controller and I'm going to make the pop up week and let's run that well test it make sure that works and by doing these exercises you should start getting the idea now of how this is working how we have a retain cycle and how we can break any one of those strong references to fix our retain cycle okay in this case since we made the pop up week when I closed it it automatically gets D initialized right away and if I dismiss the view controller we noticed that also gets D initialized or removed from memory that's fine we can make the this pop up variable week like we did and the like we did the previous two times I talked about a couple other options of making some variables nil after we're done using them and we do that right down here so remember this whole class gets passed in to the pop-up right here we're signing self to delegate so we're passing in the whole delegate view controller into the pop up through the delegate property so when you click on the select time in the pop-up it's going to call this function right here and run this code so in this code what I can do is something like this I could say pop up delegate equals nil so then when this code gets run it's going to set this delegate to nil so it no longer has a reference to this delegate view controller which will lower the retain count by 1 so remember when I click on save time it's gonna run this code it's gonna make the delegate nil and then if I dismiss they both get D initialized or removed from memory okay so that's one option I'll just comment that out for now now another option too is we can actually just set our pop up to nil like that and what that's doing is it's actually taking this property right here and it's setting it to nil so let's test that and see if that works okay so when I clicked on that button it set this pop up to nil and it D initialized it and it removed it from memory as we can see down here and if I dismiss this it also removes the view controller from memory so there you go you have many different options for breaking this retain cycle at different points we set the delegate to nil we set the pop up to nil or we can use weak references by setting the delegate as a weak reference or setting the pop up very well as a weak reference so you have some options and breaking this retain cycle all right great guys we explored a number of different ways to fix our memory problems our retain cycles and we did that by using week or unknown we also use capture list so we can change variables coming into a closure to week or unknown and we can also fix some retain cycles by setting variables to nil which manually would lower their reference count by one so they could be removed from memory and then lastly we fixed our delegate memory problem by making it week but before we could make the delegate week we had to change the protocol and inherit from any object so it restricted that protocol to just classes so at that point Swift knows that our delegate was a class or a reference type and that was the only way we could make it week was to make it a reference type because you can't make value types week or unknown all right guys thanks for watching consider subscribing if you want to see more videos like this if you want to help out the channel feel free to supply a translation for the title and the description in your native language so other people in your country can also find these videos and finally hit the like button if you like this video alright thanks guys
Info
Channel: Mark Moeykens
Views: 12,699
Rating: 4.9819412 out of 5
Keywords: Xcode, iOS, Programming, Swift, Tutorial, Tuts, Learning, Example, app, learn, make, how to, mobile, Xamarin, beginner, memory, swift memory, ios memory, xcode memory, Fixing Common Memory Problems, Reusable Popups, NotificationCenter, Callbacks, Delegates, Memory leak, retain cycle, reference cycle, capture list, capturing, automatic reference counting, retaincycle, sean allen, brian vong, best, way, easier, easy, fast, faster, development, code
Id: h5c3MDbgn-c
Channel Id: undefined
Length: 35min 51sec (2151 seconds)
Published: Thu Feb 08 2018
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.