in my app, sometimes I get a level 1 memory warning which I think is acceptable given the amount of work it is doing. When that happens, it calls the viewdidunload for one of the views which is part of the tabbarviewcontroller. In the viewdidunload, i set the outlets to nil which I think is totally normal.
The issue arises if I try to access that class again. Since it was deallocated, it will throw a bad access error which prevents me from showing that view again. If I don't set those outlets to nil then it won't crash which is normal but the convention is always to set any outlets to nul in the viewdidunload.
Any pointers for handling memory warnings in this case? I don't want to delete the code i have in the viewdidunload method since it is going against the convention.
I forgot to add that i subclassed the tabbarcontroller >_<
In that case, again the subviews added to xib file will be allocated when you load that view again. And if you want to customize something, do that in viewDidLoad method.
Where do the views which were deallocate get allocated? It sounds like you are deallocating a view in viewDidUnload that was not allocate in, for example, viewDidLoad. Instead it might have been created somewhere else, so it does not get recreated then the viewController's view is reloaded.
Related
I'm working on an app where different view controllers get pushed and dismissed via dismissModalViewControllerAnimated.
I'm having some memory issues with the app just crashing after a while. Looking at the Leaks instrument, I see that my overall allocations keeps going up and up. Even after the viewcontroller is dismissed, memory does not go down.
Are there any obvious reasons for this? What is the simplest and easiest way to find out why my app is crashing? Thanks
POSSIBLE SOLUTIONS
I went through some trial and error as well as googling and made a few changes:
1) A delegate relationship may have been retaining the viewController, so I changed the object's delegate property to weak.
2) NSTimer's should be invalidated before dismissing viewController.
3) UIView animations may interfere with dealloc being called? You can use [view.layer removeAllAnimations] to end them before popping your viewController.
If your memory is not go down after the dismissModalViewControllerAnimated .. it means you are creating the Global Object of the ViewController And after dismissing you are not setting the Object = nil;
If you set nil then your memory goes down automatically.
I decided to use storyboards in a project I have been doing. When the app launches it does the right thing of awakeFromNib and then viewDidLoad, but when the app has finished segueing to another view, it doesn't call viewDidUnload and, I think, neither does dealloc. I have used Apple's Instruments and doesn't show any memory leaking.
Just to note, I am using custom segues and testing this by inserting NSLogs into the methods. Has anyone else come across this?
Just want to update: dealloc actually is called but not viewDidUnload.
The viewDidUnload method is solely for the purposes of didReceiveMemoryWarning (i.e. when the view is being removed to recover some memory, but the view controller is not). If you want to see viewDidUnload while running in the Simulator, push or presentViewController to a secondary view, then generate a memory warning from the Simulator's menus. I quote from the UIViewController Class Reference:
When a low-memory condition occurs and the current view controller’s views are not needed, the system may opt to remove those views from memory. [The viewDidUnload method] is called after the view controller’s view has been released and is your chance to perform any final cleanup. If your view controller stores separate references to the view or its subviews, you should use this method to release those references. You can also use this method to remove references to any objects that you created to support the view but that are no longer needed now that the view is gone. You should not use this method to release user data or any other information that cannot be easily recreated.
At the time this method is called, the view property is nil.
viewDidUnload is called when the view is actually unloaded. If you want to clean up your resources when the view is not displayed put that in viewDidDisappear.
If you want to see what is happening with viewDidUnload, run your app in the simulator and from the menubar choose Hardware | Simulate Memory Warning.
Under memory pressure, views that are not on screen are removed and that is when the viewDidUnload method is sent.
I have what seems like a weird (non?) issue with UIViewController. It appears that the controller is not releasing its subviews when it is dealloc'd. I placed NSLog messages in all of the subview's dealloc method as well as the view controller. The view controller dealloc gets called but the subview's do not. However, if I then push another instance of that view controller on to the navigation stack, it appears that all of the subviews of the previous instance are then released (I get a bunch of NSLog messages in the console letting me know). I've check and I have no separate reference to the custom view controller in the presenting view controller (the one that's doing the pushing).
One small (maybe) detail: The custom view controller does receive a block it stores and then executes before popping. However, I did send nil to it and I get the same behavior. Plus, the presenting view controller does dealloc when popped of the stack, so no retain cycle.
Also, I did try explicitly releasing each view in the dealloc method of the custom view controller. Same behavior.
Is it possible the navigation controller would be holding on to it? It doesn't seem to do this to any of my other view controllers.
My problem is that this does represent a memory leak (of all those subviews); though the leak doesn't stack, it's still a leak.
Ok, this is embarrassing. I did find the problem in another class (called ViewDef) I was inadvertently using as a collection class. It was a quick and dirty way of keeping track of my subviews when I was first figuring out some animations (months ago). ViewDef stored frame/font/color/etc info retrieved from a database, so it was convenient to also store the views when figuring out animations (between orientations). These ViewDefs were being store by my model and passed around, so of course the views were also being retained (and replaced later by another view controller). Anyway, I forgot to insert a warning in my code to fix this later.
Moral of the story: If you plan on doing something stupid, at least document your stupidity so you don't have to broadcast it over the internet later.
you could try setting the subviews to nil in the viewDidUnload method maybe that will help
One thing to try is to make sure all your subviews delegates are set to nil.
I'm not entirely new to iPhone dev, but I ran across a situation where I was unsure about the best design choice for my code.
I have a view controller which asynchronously fetches an image from the internet and loads it into an image view. When the app receives a memory warning however, the imageView is released by didReceiveMemoryWarning. If the app receives a memory warning while loading an image, the imageView is nil by the time my code tries to load the image into the imageView.
What is the best practice for viewDidLoad? Should I just instantiate any variables that I may need later on? Or should I just check for nil values elsewhere in my code?
Also, in viewDidUnload, should I just set IBOutlet variables to nil? Why is this?
Thanks! -Matt
Why not just create the image view on demand when the image is loaded? No need to create it any earlier. Or, if you do, then just have code to recreate it if it has been released.
In viewdidunload you should release resources that you created in viewdidload (or load view). Including any IBoutlets that interface builder hooked up for you. It's good practice to set these variables to nil to ensure they won't be inadvertently accessed after release, or double released.
IBOutlets can be both instance variables and properties. In both case, we should release the references when the view, which is top most superview, is unloaded. Setting the IBOutlet retained properties to nil releases the reference automatically, but for instance variables we need to release them. We still need to set instance variables to nil because viewDidUnload is not dealloc, so we must comply with the managed memory rule or we might have invalid references sitting around inside the instance.
I don't think there are rules about what you should do in viewDidLoad, but if you did release and set some variables to nil in viewDidUnload, you need not have to check for nil again in the viewDidLoad unless you don't trust the framework.
If an application receives a low memory warning and a view controller releases the view, how does one reload the view the next time it's needed. I have my views defined in a .xib file and on earlier iphones, the views are being set to nil. Where/when/how do I recreate these views if they are removed?
I was writing my code pretty much horribly wrong. I was setting views to nil in viewDidUnload but all my creation was done in init. So when the application received a memory warning, when I went back to that view controller, the views were gone. This answer helped me realize my mistake; namely, that additional views could be added to viewDidLoad so that if they are released due to memory warnings in viewDidUnload, they can be recreated.