According to documentation, UIWebView delegate property needs to be set to nil before disposing of the UIWebView instance.
My question is : If the UIWebView is setup in IB with its delegate points to the file owner which is a viewController. And the UIWebView and its superview is never disposed of during the course of execution, does the delegate need to be set to nil ?
Am asking this because it seems difficult to get a reference to UIWebView.
Hope somebody knowledgeable could give some hints.
(non-arc setup)
It is always good practice to follow the suggestions in the documentation. In most cases, what you're suggesting is generally correct: there isn't really a need to set the delegate to nil as the only time the object will be destroyed is when execution ends. However, that is similar to saying "I don't need to free this heap space because it will only be freed immediately before main returns"...
A more pressing problem is what happens if the application receives a low memory warning. If a memory warning is received and the UIWebView is unloaded and the delegate is NOT set to nil, then you may end up with a memory leak behind the scenes depending on what UIWebView does when it sets up/removes its delegate.
If you set the delegate to nil on destruction, then whatever needs to happen is handled by UIWebView when it detects its delegate is set to nil. If you don't, and the instance is unpredictably destroyed (say, due to a low memory warning), then once the warning has cleared, the loading will take place again and whatever procedure is taken to set a UIWebView's delegate will be run twice, without the 'shut down' procedure ever happening in between. Given their insistence that you set the delegate to nil on destruction, I imagine this manifests itself as a memory leak or something similarly sinister.
Related
What is the significance of setting the properties to nil in viewDidUnload in both the aspects i.e. with ARC or without ARC?
Does it only matter in case of IBOulets? Don't we need to set the other properties (that are not IBOulets) to nil?
What can be the consequences if I don't do that? I haven't set them nil before and haven't observe any consequences.
Help is really appreciated.
Setting IBOutlets to nil in viewDidUnload tells the compiler to release the outlets on memory warning.because on memory warning ..viewDidUnload and didReceiveMemoryWarning of the viewcontrollers gets called..Normally in ViewDidUnload the IBOutlets are set to nil and in didReceiveMemoryWarning properties or objects are released.Hence in such a case memory is regained and thus your app can continue to function else continuous pooling causes in crash due to low memory
After reading a lot articles on the web and going through similar questions on stackoverflow.com I reached at the following understanding:
viewDidUnload is called by the compiler in low memory situation.
We should set those properties to nil in the method, which are being re-instanciated in viewDidLoad. Almost all IBOutlet components falls under the category. So better we declare the IBOutlets to nil in the method. Not doing so will lead to continuous pooling and may cause crash due to low memory in future(the time, the app continues to run).
I also read that we should not nil the instances of the class like NSString(may cause crash), which is right as per my experience. But reason for that I don't know.
It has nothing to deal with ARC. Setting a property to nil simply means that the property doesnot keep the refrence of any memory location anymore.
I will keep on updating this answer every time i came to know something more about it.
Thanks.
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.
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.
Aren't these methods called when the app is about to be shut down? If so, then won't the memory be all cleared out anyway?
If you only have one view that lasts the duration of the app, then unload and dealloc are currently never even called, so these methods are actually unused and unneeded.
However, if you ever expand this app to have views and objects that get switched in and out of use, then in low memory circumstances these methods may well be called to lower your app's memory footprint so that the app doesn't get killed for using too much memory. So leaving them in (and coding them correctly to release internally allocated objects and malloc'd memory) for future code reuse is considered good practice. That's why they come with the various Cocoa templates.
Aren't these methods called when the app is about to be shut down? If so, then won't the memory be all cleared out anyway?
It is true that viewDidUnload and dealloc are called when an app terminates, but these are certainly not the only times. It is very important to correctly implement these cleanup methods, as well as didReceiveMemoryWarning.
If you don't clean up properly in dealloc, then your app will start to leak memory. Over time, it may consume more and more memory, until it gets terminated by the system.
Similarly, if your viewDidUnload doesn't release its resources, you can be leaking memory. If the view is used multiple times, each invocation will leak.
Careful memory management is more important than ever with iOS 4, as your application may end up in the background if the user presses the Home button. This means it may run for longer than ever, and thus you will be reusing the same view controllers when it regains the foreground. If your app doesn't release unused memory properly, it will almost certainly be killed by the system.
iOS Memory Management Programming Guide
viewDidUnload is only called in low memory situations. You want to release all object you create in viewDidLoad. You want to pair them up. You still want to release everything in dealloc, since viewDidUnload will not be called if low memory situations never occur in your app.
Keep in mind that each class inerithing from NSObject has its dealloc and so when the reference count of an object reaches 0 , its dealloc is being called, meaning that the memory owned by that object would better be deallocated.
Similarly viewDidUnload is a method that each UIViewController has and it is being called when the main view associated to the controller is no more needed, no more visible if you want (you can think of it being called when you a pop the controller from a navigation stack or switch a tab in tabbar controller). It is convenient for the app and the iPhone/iPod not to have the objects owned by the view around when the view is not displayed/active/used etc.
Finally the AppDelegate, as an object has its own dealloc method, so maybe your confusion can come from this point.
According to the rules of memory management in a non garbage collected world, one is not supposed to retain a the calling object in a delegate. Scenario goes like this:
I have a class that inherits from UITableViewController and contains a search bar. I run expensive search operations in a secondary thread. This is all done with an NSOperationQueue and subclasses NSOperation instances. I pass the controller as a delegate that adheres to a callback protocol into the NSOperation.
There are edge cases when the application crashes because once an item is selected from the UITableViewController, I dismiss it and thus its retain count goes to 0 and dealloc gets invoked on it. The delegate didn't get to send its message in time as the results are being passed at about the same time the dealloc happens.
Should I design this differently? Should I call retain on my controller from the delegate to ensure it exists until the NSOperation itself is dealloc'd? Will this cause a memory leak? Right now if I put a retain on the controller, the crashes goes away. I don't want to leak memory though and need to understand if there are cases where retaining the delegate makes sense.
Just to recap.
UITableViewController creates an NSOperationQueue and NSOperation that gets embedded into the queue. The UITableViewController passes itself as a delegate to NSOperation. NSOperation calls a method on UITableViewController when it's ready. If I retain the UITableViewController, I guarantee it's there, but I'm not sure if I'm leaking memory. If I only use an assign property, edge cases occur where the UITableViewController gets dealloc'd and objc_msgSend() gets called on an object that doesn't exist in memory and a crash is imminent.
In the general case, a delegate owns the objects it has set itself as the delegate to, or at least retains references to them directly or indirectly. In the dealloc method, a delegate should either release all objects that it is the delegate of in such a way that prevents future callbacks, like NSTimer invalidate, or clear the delegate member of those objects that may persist.
While it is only convention that prevents retaining a delegate, it is convention based on good design. In your case, wouldn't the results be discarded anyway since the delegate is being disposed?
You can make the delegate property of your NSOperation atomic by not setting the nonatomic flag and synthesizing the getter and setter. Or you can use performSelectorOnMainThread before using the delegate member.
To recap, there is usually a better solution than retaining the delegate.
I really wonder though if the "don't retain your delegate" rules still apply specifically to multi-threaded object/delegate relationships, especially the one you're writing about. I found my way to this question because I'm in exactly the same situation: I'm running a finite but unpredictable length asynchronous network operation (it will finish and self-terminate "sometime soon") in an NSOperation, and using a delegate stored in the NSOperation to notify completion of the operation back to the original requestor object.
Retaining the delegate in this case makes perfect sense to me. The operation will complete eventually, it will call the "done" method on the delegate, and then will release the delegate and self-terminate. If the calling object would have been dealloc'd while the NSOperation was running, it just sticks around a little longer until the operation completes.
Note that marking the delegate property atomic as drawnonward suggests is in itself not sufficient to protect against race conditions! Setting a property as atomic only means that a reader or writer of the property will write or read an entire whole value at a time, nothing else. The following sequence would result in a crash:
(NSOperation thread): NSOperation has finished and is preparing to notify the delegate object . The code atomically reads the value of the delegate property preparing to call its "done" method.
----> Thread switch to main
(main thread) the requesting object is dealloc'd, and in -(void)dealloc (atomically!) sets the NSOperation's delegate property to nil, dealloc finishes, and the object is now gone
-----> Thread switch to NSOperation thread
(NSOperation thread) calls [delegate allDone] and the program crashes (if you're lucky) because the delegate object is gone. If you're lucky. Maybe something else was allocated in that space in the meantime and now you have unpredictable corruption, what fun!
The key thing here is that the retain cycle is temporary by its very nature -- the NSOperation will complete and clean up all on it own. It's not like a UITextField and a UIViewController holding retained references to each other that will never go away and thus leak the memory.
It seems to me that retaining the delegate in the case of an asynchronous, limited, self-terminating operation is the cleanest and most robust implementation.
EDIT:
if you absolutely must not retain the delegate because it causes memory problems, then instead the delegate object and the operation object must both use an explicit locking mechanism to synchronize access to the delegate pointer stored in the operation. "atomic" properties do not provide a thread-safe protocol to marking the delegate pointer nil.
But I think this gets really complicated and full of race conditions. I think at the very least that you must in the delegate object's dealloc, run a locking protocol to make sure that the operation's delegate pointer is safely set to nil so that arbitrary thread interleaving cannot under any circumstances call to a dealloc'd delegate object.
Slavish adherence to rules sometimes makes things way more complicated than they need to be. It's best to understand what the rules are, why they are there, and so you know when it makes sense (like I believe it does in this very particular scenario) not to follow them, and what the advantages/disadvantages are of doing so.
I approach this a little differently, and I do not retain delegates.
If I need to lose the view, I cancel the NSOperation. I believe that its good design that if a thread's return value has nowhere to go, then the thread should stop.
I also have seen the edge case where the thread cannot be stopped. In this case, I nil the unretained delegate, just to be sure, and check that the delegate is not nil before making the callback.
Holding on to an object, even temporarily, when its no longer needed, especially on the iPhone chews up memory and, in my opinion, is bad design.
As per #Bogatyr's answer, a simple check for nil before calling [delegate allDone] is much cleaner.
You could retain the UITableViewController at the beginning of the NSOperation and release it at its end.
Alternatively, you could set it to nil after it is released so the dangling call from the NSOperation won't crash your program.
Depending on how the NSOperation is executed you could also autorelease the UITableViewController instead of releasing it, so the NSOperation can still use it.
However, all these things really only cure the symptoms, not the illness. The correct way to do it is outlined by drawnonward.