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.
Related
I've learnt that if you create an object, you own it and need to release it when you're done with it. In this case I create an UIImageView and add it to my view like this:
myImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"image.png"]];
[myImageView setFrame:CGRectMake(10,10,100,100)];
[self.view addSubview:myImageView];
[myImageView release];
If I later on want to detect touch on myImageView like this in my touchEnded-method:
if([touch view] == myImageView){
NSLog(#"TOUCHED!");
}
This works, but is this correct, since I now used myImageView after releasing it?
And how do I release myImageView from the self.view that I added it to earlier?
The cardinal rule of Cocoa memory management is that you should retain those things you care about, and release those things you do not care about. There are a very small number of exceptions to prevent retain loops (delegates and datasources are never retained), but this is the rule you should follow.
In this case, if you are storing the image in an ivar, I would continue to retain it, regardless of the fact that its superview will always retain it, and so you don't "have" to. But if the view is removed from its superview, you will wind up with a dangling pointer, and then you will crash, so I code defensively against that by retaining. If you used an accessor here (which you should), then this would be automatic and much safer.
Apple has grown more consistent about this in iOS, switching their recommendation about IBOutlets. On Mac, you do not retain your IBOutlets, but in iOS, Apple explicitly instructs you to do so. This is similar to the situation you are discussing, and I agree with Apple's move towards a safer approach.
As long as your myUIImageView object has a retain count > 0, it will still exist and you can continue using it. When you first add it as a subview, it gets a retain message so it's retain count is likely 2. Then you send it release, so its retain count is reduced to 1. This means it still exists in memory. Now, if you sent it release again, or sent it removeFromSuperView then its retain count would be zero, and you'd lose it.
The behavior is erratic, sometimes you may see it works, sometimes you get a crash.
If you want to use that variable to point to your image view, retain the ivar (by using a retain property). This way you ensure that the image view is available for your controller class to use.
Assuming myUIImageView is an ivar of your custom UIView subclass, your code will work as long as your image view stays in his superview. The image view instance may be deallocated and you may end with an invalid pointer referencing a deallocated object. At best you crash.
According to memory management guidelines, you should consider your image view <-> custom uiview subclass relation as wether:
strong reference. You own the image view (as you created it), and are responsible for retaining / releasing it
weak reference. You don't own the object, thus keeping a reference to it may be dangerous.
in your case, it's probably a strong reference. your myUIImageView ivar should be a nonatomic retained property of your object.
If you need to access your UIImage at some point in the future, you need to retain it.
How you do this is at your discretion, but you should not rely on the UIView to retrain your objects for you. The situation you've created works for now, but it's fragile.
i have this in the superview:
mySubView = [[MySubView alloc] init];
[self addSubview:mySubView];
[mySubView release];
then at some point later, in the sub view, this:
[self removeFromSuperview];
when i debug it, i notice that the dealloc for the subview is never called, even though i'm fairly sure the reference count should be 0. any ideas why this might be? thanks.
Yes, When you send removeFromSuperview, subsequently release message sent. And each call of release decrease retain count. And in case:
MySubView * mySubView = [[MySubView alloc] init];
[window addSubview:mySubView];
[mySubView release];
[mySubView removeFromSuperview];
the result will be as you expect: after removeFromSuperview mySubView's retain count became 0, and dealloc will be called.
but in your example there is more code behind "then at some point later" and some other object (and it is not superview) retains mySubView. You can for example log retainCount value to see where your view retained/relesed.
You can't rely on retainCount to provide any useful information for debugging. In some cases it may assist you, but the Apple docs say:
Important: This method is typically of no value in debugging memory management issues. Because any number of framework objects may have retained an object in order to hold references to it, while at the same time autorelease pools may be holding any number of deferred releases on an object, it is very unlikely that you can get useful information from this method.
To understand the fundamental rules of memory management that you must abide by, read “Memory Management Rules”. To diagnose memory management problems, use a suitable tool:
The LLVM/Clang Static analyzer can typically find memory management problems even before you run your program.
The Object Alloc instrument in the Instruments application (see Instruments User Guide) can track object allocation and destruction.
Shark (see Shark User Guide) also profiles memory allocations (amongst numerous other aspects of your program).
I don't know if removeFromSuperview does this, but the pointer in the superview needs to be released. If removeFromSuperview only releases the pointer in the subview to the superview and not the pointer in the superview to the subview, that may be the source of your problem.
i'm fairly sure the reference count should be 0.
Are you? Why?
Forget retain counts. Think only in terms of object ownership because retain counts are almost completely meaningless as an aid to debugging memory management.
Apple provides lots of tools for debugging memory management issues. Use them.
This link might be helpful.
http://developer.apple.com/iphone/library/documentation/Performance/Conceptual/ManagingMemory/Articles/FindingPatterns.html
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.
Whenever I add a new viewController my ObjectAlloc jumps up really high and never comes back down. Even after calling removeFromSuperview. Is this normal?
if((UIButton *) sender == gameArcadeBtn) {
GameArcade *gameArcadeController = [[GameArcade alloc]
initWithNibName:#"GameArcade" bundle:nil];
self.gameArcade = gameArcadeController;
[gameArcadeController release];
[self.view insertSubview:gameArcadeController.view atIndex:1];
}
Instantiating a view always creates many objects.As long as this view is in memory or has not been autoreleased, the objects will remained alloced in memory. Thus, to answer your question, this is normal.
It sounds like you are worried about memory usage and while it is important to watch the object allocs so that it doesn't get too it is more important to find your app leaks.
Some memory management tips:
1) do lazy loading. Only load your views when the user asks for them, not all at the beginning of the app
2) remove everything that you possibly can when you dont need it anymore. This means doing tons of work in viewWillAppear and viewDidDisappear
3) learn about #properties and how it relates to autoreleasing, and do not use properties for everything.
4) As appealing as it is, avoid autorelease and manually release objects when you dont need them anymore.
that's probably due to the fact that you're still retaining the view's controller in the class. try releasing that
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