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

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.

Related

Deallocation when iPhone is turned back on from sleep and app is still open

There are a few instances where my app crashes when turned back on from sleep and the app is still open. It happens if a UITableViews is open when the iPhone is put into sleep; the table is being filled from an XML being parsed. I understand why it is crashing; in my viewDidUnload method I am releasing the array that is filling the table. How to handle this I am not sure; yes I could simply not release it in viewDidUnload, but then it would never leave memory if you returned to the main menu.
Any help would be appreciated!
The method viewDidUnload is not the right place to release your data if at all. I quote Apple's documentation, which desribes it better than I could:
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.
ViewDidUnload is used only to release view related objects. A view controller can release its view because its not shown, still your instance of that controller exists and so does your model.

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.

Cannot allocate new instance of UIView subclass after deallocating

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.

Iphone: which event is called when a view is unloaded?

The question is all in the title. I want to execute some cleanup code when one view in my application gets unloaded. Is it possible to do so? If so, which is the event that I should intercept?
-viewDidUnload() or -viewWillDisappear() depending on your design. You probably want to go for viewDidUnload().
It will also depend on the sdk you are using. If you are using iphone-sdk 3.x then viewDidUnload will be called otherwise it will not get called. while viewWillDisappear is called in 2.x and above. Now still if you want to call a method only when the view is unloaded you can call it from the dealloc but it will not be highly trustable.
You'll want to take a look at viewDidUnload or viewWillDisappear, for cleanup you'll probably use viewDidUnload, you may also want to just do cleanup in the dealloc method.
From Apple's documentation:
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.
viewWillDisappear: Notifies the view
controller that its view is about to
be dismissed, covered, or otherwise
hidden from view.
- (void)viewWillDisappear:(BOOL)animated
Parameters animated If YES, the
disappearance of the view is being
animated.
Discussion This method is called in
response to a view being removed from
its window or covered by another view.
This method is called before the view
is actually removed or covered and
before any animations are configured.
Subclasses can override this method
and use it to commit editing changes,
resign the first responder status of
the view, or perform other relevant
tasks. For example, you might use this
method to revert changes to the
orientation or style of the status bar
that were made in the
viewDidDisappear: method when the view
was first presented. If you override
this method, you must call super at
some point in your implementation.

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