Cannot allocate new instance of UIView subclass after deallocating - iphone

I have an instance variable *TangramLevel(:UIView) currentLevel; in my viewController class, and I have an instance allocated at start (it's retainCount 1). I attached it to the view [self.view addSubview:currentLevel]; (retainCount 2).
When the level finishes, it notifies the controller, so controller removes it from the view [currentLevel removeFromSuperview]; (retainCount 1), and release the allocated instance - [currentLevel release]; -, to have it deallocated (retainCount 0 = invoke dealloc).
Then on the next line, controller wants to allocate/addSubview a new level instance with another level data, but the application crashes (EXEC BAD ACCESS).
If I don't release currentLevel after removeFromSuperview, the appliaction works, but I have an unused level instance left in memory then, which is also a problem (the main problem itself).
Is there any bug in the method I wrote above? Or the bug is elsewhere, maybe in the level class? I allocated some UIImageView in the level, but I release every allocated object in the levels dealloc method. Any ideas?

Post your code.
This is definitely a memory management issue. The question is "where is the problem created?" To answer that, we need to examine the following:
Your "currentLevel" iVar handling code (do you used synthesized properties, etc.). Post it.
How are you assigning the view to currentLevel?
Where are you releasing this, specifically?
How is your view's dealloc implemented (what do you release and how)?
Is there any other code that retains/releases this view or anything related to it?
The fact that you're calling release in your "I'm done with this level, let's swap in the next" code suggests an overall design issue. Make the memory management of each of a class's iVars the responsibility of its accessors and ONLY use the accessors to interact with it (even from within the class/instance). With synthesized properties, this makes it brain-dead-simple. That way you don't have to worry about where to retain/release iVars because it's always funneled through the accessors.

Related

Cocoa touch - Memory Management

I am a former java programmer, and I am having some troubles managing the memory on cocoa touch.
In fact, I think I got the retain/release trick, but still I am not sure I got it right.
For example, I am creating and adding a subview to the main window:
aViewController=[[AViewController alloc]init];//aViewController is (nonatimic,assign), so retaincount = 1 after this line?
[self.window addsubview aViewController];
[aViewController release];//retaincount=0?
And in aViewController I have an IBAction:
[self.view removeFromSuperView];
How can I be sure the object aViewController gets completely 'deleted' and memory released after I removed it from superview (think that controller as a graphic-heavy view controller)?
Also, generally, is there a way to be sure an object is deallocated? I am aware that if I ask ownership of an object I have to release it at a certain point, but what if I just want the object's pointer to be null at a certain point(not basing on the retaincount)? Should I call dealloc directly? I find sometimes very confusing to keep under control the retain/release mechanism.
If someone could give me a quick breakdown to make my mind 'click', i would be extremely grateful.
Thanks.
The short answer is you shouldn't worry about when an object gets deallocated (unless you are debugging a memory management problem). You should just worry about ensuring that if your code retains, copies or inits an object, it releases or autoreleases it. By doing so you will ensure reference counts are properly maintained and hence deallocation will be managed for you.
Leave the task of deciding when to dealloc an object to the runtime. Never call dealloc directly unless you are calling the super classes dealloc method at the end of your objects dealloc method.
Also, don't even look at the retain count property of an object. Various pieces of the framework manipulate those too during the lifetime of the object, and you'll see that number move around seemingly at random. It'll just drive you nuts.
The really important thing is to make sure you've got the objects retained that would be a problem if they went away suddenly, and released when you're okay with them going away suddenly.
aViewController=[[AViewController alloc]init];retainCount is 1
[self.window addsubview aViewController.view];retainCount is 2 (adding the view increments the retainCount)
[aViewController release];retain count decrements to 1;
[aViewController removeFromSuperView];retain count decrements to 0;
Now the dealloc method will be called the allocated memory will be freed. This is what have understood please correct me if i am wrong i always find difficulties during memory management.

didReceiveMemoryWarning, viewDidUnload and dealloc

I've looked through lots of posts, my books and Apple Developer and gleaned most of the understanding I need on use of these. I would be really grateful if some kind person could confirm that I've got it right (or correct me) and also answer the two questions.
Many thanks,
Chris.
Order of Messages
Generally, the messages will appear in the following order:
didReceiveMemoryWarning
viewDidUnload (which can be caused by 1) - obviously only applies to View Controller Classes.
dealloc
didReceiveMemoryWarning
Called when the system is low on memory.
By default, view controllers are registered for memory warning notifications and within the template method, the call to [super didReceiveMemoryWarning] releases the view if it doesn't have a superview, which is a way of checking whether the view is visible or not. It releases the view by setting its property to nil.
Action - Release anything you do not need, likely to be undoing what you might have set up in viewDidLoad. Do not release UI elements as these should be released by viewDidUnload.
Question1 - It seems that this will be called even if the View is visible, so its difficult to see what you could safely release. It would be really helpful to understand this and some examples of what could be released.
viewDidUnload
Called whenever a non visible View Controller's View property is set to nil, either manually or most commonly through didReceiveMemoryWarning.
The viewDidUnload method is there so that you can:
- clean up anything else you would like, to save extra memory or
- if you've retained some IBOutlets, to help free up memory that wouldn't otherwise be released by the view being unloaded.
Action - generally any IBOutlets you release in dealloc, should also be released (and references set to nil) in this method. Note that if the properties are set to retain, then setting them to nil will also release them.
dealloc
Called when the view controller object is de-allocated, which it will be when the retain count drops to zero.
Action - release all objects that have been retained by the class, including but not limited to all properties with a retain or copy.
Popping View Controllers and Memory
Question 2 - Does popping a view remove it from memory?
Some corrections and suggestions:
didReceiveMemoryWarning practices
As you said, the controller's default implementation of didReceiveMemoryWarning releases its view if it is 'safe to do so'. While it's not clear from Apple's documents what 'safe to do so' means, it is generally recognized as it has no superview (thus there is no way that the view is currently visible), and its loadView method can rebuild the entire view without problems.
The best practice when you override didReceiveMemoryWarning is, not to try releasing any view objects at all. Just release your custom data, if it is no longer necessary. Regarding views, just let the superclass's implementation deal with them.
Sometimes, however, the necessity of the data may depend on the state of your view. In most cases, those custom data is set in viewDidLoad method. In these cases, 'safe to release custom data' means that you know that loadView and viewDidLoad will be invoked before the view controller uses the custom data again.
Therefore, in your didReceiveMemoryWarning, call the superclass implementation first, and if its view is unloaded, then release the custom data because you know that loadView and viewDidLoad will be invoked again for sure. For example,
- (void)didReceiveMemoryWarning {
/* This is the view controller's method */
[super didReceiveMemoryWarning];
if (![self isViewLoaded]) {
/* release your custom data which will be rebuilt in loadView or viewDidLoad */
}
}
Be careful not to use self.view == nil, because self.view assumes that the view is needed for someone and will immediately load the view again.
viewDidUnload method
viewDidUnload is called when the view controller unloaded the view due to a memory warning. For example, if you remove the view from the superview and set the view property of the controller to nil, viewDidUnload method will not be invoked. A subtle point is that even if the view of a view controller is already released and set to nil by the time the controller receives didReceiveMemoryWarning, so actually there is no view to unload for the controller, viewDidUnload will be invoked if you call the superclass's implementation of didReceiveMemoryWarning.
That's why it's not a good practice to manually set the view property of a view controller to nil. If you do, you may better send a viewDidUnload message as well. I guess your understanding of viewDidUnload is more desirable, but apparently it's not the current behavior.
Popping view controllers
If you mean 'removing from the superview' by 'popping', it does decrease the retain count of the view, but not necessarily deallocate it.
If you mean popping out from a UINavigationController, it actually decrease the retain count of the view controller itself. If the view controller is not retained by another object, it will be deallocated, desirably with its view. As I explained, viewDidUnload will not be invoked this time.
Others...
Technically, the retain count may not go down to zero. The object is more likely to be just deallocated without setting the count to zero beforehand.
Just to make sure, the view controller itself is normally not deallocated by default behaviors due to the memory warning.
didReceiveMemoryWarning
...
Action - Release anything you do not need, likely to be undoing what you might have set up in viewDidLoad.
This is wrong. Anything that you recreate in viewDidLoad should be released (and set to nil) in viewDidUnload. As you mention below, didReceiveMemoryWarning is also called when the view is visible. In didReceiveMemoryWarning, you should release stuff like caches or other view controllers you are holding on to that can be recreated lazily the next time they are required (i.e., by implementing their getter manually).
viewDidUnload
...
Action - generally any IBOutlets you release in dealloc, should also be released (and references set to nil) in this method. Note that if the properties are set to retain, then setting them to nil will also release them.
Correct. Generally, everything you create in viewDidLoad and all IBOutlets that are declared as retain should be released and set to nil here.
dealloc
...
Action - release all objects that have been retained by the class, including but not limited to all properties with a retain or copy.
Correct. It's worth noting that this includes all objects you handle in viewDidUnload because the latter is not implicitly called in the dealloc process (AFAIK, not entirely sure). That's why it is essential to set all releases objects to nil in viewDidUnload because otherwise you risk releasing something twice (first in viewDidUnload, then again in dealloc; if you set the pointer to nil, the release call in dealloc will have no effect).
Popping View Controllers and Memory
Question 2 - Does popping a view remove it from memory?
Not necessarily. That is an implementation detail that you should not be concerned about. Whatever the current practice is, Apple could change it in the next release.
Just to update this thread to make it iOS6-relevant:
viewDidUnload and viewWillUnload were deprecated in iOS6. These methods are never called.
For this and other deprecated methods, see: http://developer.apple.com/library/ios/#documentation/uikit/reference/UIViewController_Class/DeprecationAppendix/AppendixADeprecatedAPI.html
Form iOS 6 onwards, how we can check whether the view loaded again. Since "viewDidUnload" is deprecated. Are you sure "loadView" and "viewDidload" will call if the view is getting removed after "didReceiveMemoryWarning" warning.

iPhone: Does it ever make sense for an object to retain its delegate?

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.

Data initialized during 'init' methods after low memory condition handling

When an application comes back from low memory conditions (ie there was low memory, things were freed and the app is now back to normal use scenario), what happens to the state of objects that were initialized and set up via
-(id)init
method?
When you receive low memory warnings, you persist all of the data and the viewDidUnload method is invoked. Eventually, the view maybe reloaded but the class's 'init' method is not called a second time.
So would you persist any state information you have initialized in the 'init' method and later manipulated during the course of the use of the application?
What I'm asking more specifically is whether classes & other related data created during the 'init' method would be reinstated when coming back from a low-memory condition.
If an object is dealloc'ed you got a memory alert, then you have to do the init again. It will not be done for you.
The system will typically not dealloc your objects unless you do so by releasing. You could do nothing in response to a memory alert or you could release some views and with them, some of their owned objects.
viewDidUnload is a way for you to know whether your view was unloaded in which case you should go through and free and cleanup the things that you did in viewDidLoad. When your view comes back up, viewDidLoad will get called again and you get a chance to redo all the initialization.
viewDidUnload is probably what you really need study:
viewDidUnload Called when the
controller’s view is released from
memory.
(void)viewDidUnload
Discussion This method is called as a
counterpart to the viewDidLoad method.
It is called during low-memory
conditions when the view controller
needs to release its view and any
objects associated with that view to
free up memory. Because view
controllers often store references to
views and other view-related objects,
you should use this method to
relinquish ownership in those objects
so that the memory for them can be
reclaimed. You should do this only for
objects that you can easily recreate
later, either in your viewDidLoad
method or from other parts of your
application. You should not use this
method to release user data or any
other information that cannot be
easily recreated.
Typically, a view controller stores
references to objects using an outlet,
which is a variable or property that
includes the IBOutlet keyword and is
configured using Interface Builder. A
view controller may also store
pointers to objects that it creates
programmatically, such as in the
viewDidLoad method. The preferred way
to relinquish ownership of any object
(including those in outlets) is to use
the corresponding accessor method to
set the value of the object to nil.
However, if you do not have an
accessor method for a given object,
you may have to release the object
explicitly. For more information about
memory management practices, see
Memory Management Programming Guide
for Cocoa.
By the time this method is called, the
view property is nil.
Special Considerations If your view
controller stores references to views
and other custom objects, it is also
responsible for relinquishing
ownership of those objects safely in
its dealloc method. If you implement
this method but are building your
application for iPhone OS 2.x, your
dealloc method should release each
object but should also set the
reference to that object to nil before
calling super.

What sort of cleanup work should I do aside from dealloc in an iPhone app?

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