i am new to ios development. I just came to know that their are two delegates in Foundation that are retained. My understanding says that a delegate must always be loosely coupled. So why these two are specifically retained?
Thanks
Commonly, a delegate is needed for the entire lifetime of an object, which is why it is usually a weak/non-retained reference. Otherwise, neither the object nor the delegate could ever be deallocated, because they would both wait for the other to go away.
In the case of NSURLConnection and CAAnimation however, the delegate is actually only needed for a specific task that has some sort of "finished" state. When the connection finishes loading/is cancelled or the animation has reached its end, they can just release the delegate themselves and thus break the retain cycle.
Related
I have an NSOperation running not in the main thread. It is spawned from a UITableViewController. When the operation is complete, I'd like to reload the tableview since some data has changed. I've set a delegate for the background to notify on completion. When done, I call a wrapper around reloadData specifically on the main thread using performSelectorOnMainThread.
For the most part, this works well, however, there is a non-0 chance that the original (edit)tableViewController (/edit) gets released and I get zombie calls.
So the question is in 2 parts:
Is it possible to have a delegate from the background thread without retaining the object?
Is this just a bad design? Should I be using NSNotifications instead? Would that be the preferred method of notifying in this case?
Thanks in advance.
A delegate should be retained if there is a possibility that it might be released before any operation on the delegate is invoked. You can set up a state in tableViewController to handle the case when the delegate callback is invoked and the tableViewController is not to be used (Basically make the callbacks act as no-op). Once your operation is done, just release the delegate object.
It is not a bad design but you just need to handle these conditions.
I subclass MPMoviePlayerController. In that class I attached all possible notifications that I need. DidFinishPlayback, ExitFullScreen etc. in it's loading method. My question is, if I want to STOP movie and dismiss movie player view can I (and do I need to) remove observers in moviePlayerPlaybackStateDidChange method on stateStopped? What can happen if I don't do that?
The most important place to remove an observer of any kind is in the dealloc method. It is best practice to remove the observers as soon as your done observing but absolutely needs to be done by dealloc.
The reason for this is because if you register as an observer for something and your class gets deallocated the object you were observing could possible try and callback to the now deallocated object. More than likely this will cause an EXC_BAD_ACCESS and close your application.
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.
I have 2 or 3 views in my iPhone application where I have various pieces of functionality that use delegates. In all cases the delegates are assigned to "self", responding specifically to that view's actions and interacting with instance variables.
However, if I do something that takes a bit of time with a delegate, and leave the view, obviously it crashes my app as the delegate methods get called on a view I've left.
Typically in my delegate methods I am doing things like interacting with IBOutlets, calling other instance methods, saving data to Core Data etc...
How can I work with delegates better? Is what I'm doing typical, or not?
Thanks for any guidance!
Depends on the use case. If, for example, you've got a UINavigationController that manages a ViewController that use something such as Location Services, when you pop the View Controller off of the stack you're going to want to set the CLLocationManager's delegate to nil. You can do this in the dealloc method.
Can you give a specific example of an issue you're facing?
I've encountered this situation once when dealing with MapKit (race conditions involving delegate callbacks and delegate deallocation). However, in general, I think it's an indication of a bad design decision when your delegate becomes invalidated as a result of race conditions, but I could be wrong.
Typically, the objects that make use of your delegate should exist within the context of the delegate itself. So, for example, the same class that contains various IBOutlets that you want to manage with delegate callbacks should also be the delegate of those IBOutlets. That way, when the class (i.e., the delegate) is deallocated, the IBOutlets are (hopefully) also deallocated, so they won't be making callbacks to anything.
bpapa's right. More generally, if you have a potentially lengthy delegate callback, either make sure that 1) the delegate is an object that won't be deallocated during the lifecycle of the delegator (e.g., UINavigationController managing UIViewControllers) or 2) the delegator's delegate object is set to nil during the delegate's deallocation.
... That last sentence was a mouthful. :)
Your delegates should be declared as
#property (nonatomic, assign) id <MyDelegateProtocol> delegate;
This ensures a weak reference so that when you dealloc the object which has delegate, it only removes the reference and NOT the corresponding object. using (strong) will cause a crash on dealloc.
When calling your delegate, you can check
if (self.delegate && [self.delegate respondsToSelector:#selector(MyDelegateProtocolCallbackMethodName:)] {
[self.delegate MyDelegateProtocolCallbackMethodName:result];]
}
Generally I use delegates for proxy classes which fetch data from a server or edit screens like changing the title of a task in a todo list or editing a model in a database.
The advantage of a delegate is a very clear use case. If you find that multiple parts of your app need to be aware of when an event happens (triggered on 1 screen but perhaps listened for in another), I suggest using NSNotificationCenter to raise notifications, instead of or perhaps in addition to sending a message to your delegate.
Right now, I do most of my cleanup work in dealloc (cleaning up IBOutlets, allocated objects, etc.). What other places should I do cleanup work in order for my app to be a well-behaved one? Could you explain the things that are typically done in those methods as well?
For example, viewDidUnload, applicationWillResignActive, etc.
For views, I typically release any UI widgets that were created from the NIB file in viewDidUnload. Any models or other objects I clean up in the viewController's dealloc.
Sometimes I have views that create a model (say a Dictionary of section names to section rows) from a primary data object. If I create/build an object in viewDidLoad I will release it in viewDidUnload (since my viewDidLoad will get called again when the time is right).
I believe that in SDK 3+ you don't have to typically worry about implementing didReceiveMemoryWarning directly as the new viewDidUnload method is the main place to do your view cleanup.
For normal objects (objects without special life cycles like a view controller has) I just release their member vars in the dealloc.
Don't forget:
- (void)didReceiveMemoryWarning
Note: This "Answer" is only relevant to app quit/termination.
According to the answer I received to my question, it's not even necessary at all to do cleanup work like cleaning up IBOutlets, allocated objects, etc. Just save state (as necessary) when your app quits, and let the iPhone OS handle the final cleanup.
Note that your question is ill-formed. The -dealloc method of UIApplication is never called. The -dealloc of your application's delegate is never called. That means that any objects that are retained by your application's delegate will never be released, so their dealloc is never called.
You should be doing your cleanup in your application delegate's applicationWillTerminate:
Since your application is about to die, you don't really need to do anything except give back non-memory resources, make sure your data files are properly closed, and that your NSUserDefaults are synchronized so you can restart properly the next time you are run.
However, any object that might be allocated and deallocated repeatedly over the life of the program deserves a proper Obj-C dealloc method, as documented by Apple, and it is good practice to write this for all your classes, even though they won't be called, just so you build good habits, and readers won't be confused. Also, it saves maintenance headaches in the future, when you DO create and destroy multiple of these, for example in your unit tests.
I would use the [yourObject release] method, but replace yourObject with an object