The leaking of views and other objects - objective-c

I have always found this confusing. Can someone please explain it for me?
In a view controller class, I have, for example, a scroll view and I want to add a number of views to it. So I create, within the view controller, a helper method that creates the view and returns it to the caller. The caller in turn adds this new view to the scroll view.
So here I call the method to get a view and pass it directly to addSubView:
[scrollView addSubView:[self getView]];
And here is the method that creates the view:
-(UIView *)getView {
UIView *v = [[UIView alloc] init];
// do all the guff I need to configure the view
return v;
}
The question is where or when does v get released? Should I be assigning it as autorelease when it is created in getView or do I release it after i have called addSubView:? Thanks.

Since -[UIView addSubview:] obtains ownership of the passed-in view by sending it a -retain message, you should be sending the return value of getView an -autorelease message:
return [v autorelease];

Jacob's conclusion is correct, but his reason is wrong, or at least unclear (and I tried but failed to squeeze this into a comment).
In getSubview:, you have created an object. You are now the owner*. When the method ends, you must release the object, relinquishing your ownership, or it will leak. The fact that you are returning the object from the method makes no difference to this rule.
In this case, since you want the code which called this method to be able to do something with the object, you use autorelease, saying "I relinquish my ownership, but don't actually get rid of this object until the calling code has a chance to see whether it wants to make a claim". What happens on the other end (retain, copy, immediate release, anything) is of no concern for the code in getSubview:. All that method needs to do is worry about the memory that it has ownership of. Doing otherwise, i.e, relying on the fact that the calling code will do something specific with the memory, will lead to bugs.
In slightly more technical terms, the object pointed to by v must be either released or autoreleased because v is about to go out of scope at the end of the method. Once a name goes out of scope, you must no longer use that name to refer to an object, and if that name was the only reference you had to the object, you have leaked.
*Note that an object can have multiple owners -- any other object which retains it becomes a part-owner.

Related

Remove Object From SuperView but Memory still allocated? (ARC)

I'm having some issues with my application's memory management. I allocate an NSView class and put it on one of my Windows. After some time, I remove the object from the superview and then put another object in it's place. The problem that I'm having is that the memory isn't freed when I remove it. It continues to hold as much memory as it previously held, and if I add more of that class, it continues to pile onto the memory. My question is, will removing that object get rid of all objects that the class held, or are some pointers being held onto, even after the object is removed? I can post code if necessary.
Thanks!
Edit:
Here's the code that I use to allocate it
MyClass *theClass1 = [[MyClass alloc] initWithFrame:frameRect];
[self.window.contentView addSubview:theClass1];
Here's the code that I use to deallocate it
[[self.window.contentView subviews] makeObjectsPerformSelector:#selector(removeFromSuperview)];
I create it on a timer, updated every minute. I do not reference it anywhere else.
The memory for each object should be treated separately for each instance of a class. Removing one instance should not affect the allocation of other instances (unless you had some custom code looking for other instances of the class).
As for the NSView removal, removeFromSuperview does release the receiver (the view being removed), which is why the Apple docs on NSView say to retain it if it is still needed later.
Posting the code where it is declared, instantiated, added, then removed would be helpful for a more specific answer.

Prevent instance variables from being cleared on memory warning

I've got a relatively simple problem that's been evading solution for some time. I have a view controller and an associated XIB. The view controller is called FooterViewController. FooterViewController's view is set as the footer view of a tableview.
FooterViewController's view includes a label for showing feedback to the user. I would like this label to persist until its value is changed by my application. Under normal circumstances, it does. However, I've just begun testing with memory warnings, and I've found that after the view is unloaded in response to a memory warning, the label is cleared.
Here's what I've tried so far to solve the problem: in FooterViewController's viewWillUnload method, I store the label's text in an instance variable called statusString:
- (void)viewWillUnload
{
statusString = [statusLabel text];
testInt = 5;
NSLog(#"View will unload; status string = %#; testInt = %d",
statusString, testInt);
[super viewWillUnload];
}
Note that I've also set another instance variable, declared as NSInteger testInt, to 5.
Then, in FooterViewController's viewDidLoad method, I try to set the label's text to statusString:
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"Just before setting label, statusString: %#; testInt: %d",
statusString, testInt);
[statusLabel setText:statusString];
NSLog(#"View did load.");
}
However, this does not work. Further, in the log after simulating a memory warning, I see:
View will unload; status string = Invalid IP address Error code: 113; testInt = 5
(Note that "Invalid IP address Error code: 113" is the correct value for statusString)
Then, after navigating to FooterViewController again, I see:
Just before setting label, statusString: (null); testInt: 0
This indicates to me that for some reason, the instance variables of FooterViewController are being reinitialized when the view loads again. A final note: the method initWithNibName:bundle: is being called each time the view must reload, though I expect this; after all, the view must be reloaded from the NIB.
So, my questions are these:
Why do these instance variables appear to be nullified or zeroed in the process of unloading and reloading the view?
If I'm doing something incorrectly that's causing this nullification, what is it?
If I'm not doing anything incorrectly, and this is normal behavior, how should I handle maintaining state between loads of the view?
Thanks,
Riley
The statusString looks like a weak reference, not a strong property. It can not retain the labels's text, which gets deallocated with the label when the view is unloaded. That's why you get first a correct value (before the label is deallocated), and null later (after the label has been deallocated, and the weak ref nullified). Turn your statusString into a strong property, and that ARC magic won't bite you any longer.
It looks like you need to be using didRecieveMemoryWarning instead of viewDidUnload, since viewDidUnload is not guaranteed to be called in the event of a memory warning. If the crash is exiting the app completely then you need to be writing the data to disk using something like coreData. Save your data here and then call the super so the view will still be released. Hope that helps.
I figured out what was going on, finally. The issue was that I called the allocation and initialization methods for FooterViewController in its parent view controller's viewDidLoad method. When the views were dumped and subsequently reloaded, my view controller was re-initialized! This destroyed the original FooterViewController, which maintained the instance variables I needed, and replaced it with a brand-new VC.
The solution was to move [[FooterViewController alloc] init] to the init method of FooterViewController's parent VC, so that the initialization was only performed once per run cycle.
I've learned my lesson: don't reinitialize your view controllers unless you really mean to do so. As such, be very careful where you put your calls to the initializer in parent view controllers.
Thanks for the help I got from the two answerers.

Large retain count with a recent created object. Objective-C

I'm getting an strange case of excessive retain counts for a view controller that I'm loading when a button is pushed.
This is the code:
-(IBAction)new
{
if (!viewSpace)
viewSpace = [[ViewSpace alloc] initWithNibName:#"ViewSpace" bundle:nil];
viewSpace.delegate = self;
viewSpace.view.frame = CGRectMake(0, 0, viewSpace.view.frame.size.width, viewSpace.view.frame.size.height);
[self presentModalViewController:viewSpace animated:YES];
NSLog(#"Count Retain: %d",[viewSpace retainCount]);
}
-(void)viewSpaceWasDissmissed:(id)sender
{
[self dismissModalViewControllerAnimated:YES];
[viewSpace release];
NSLog(#"Count Retain: %d",[viewSpace retainCount]);
}
When the IBAction New is executed first time, the retain count is 5 when just is created. (It must be 1).
When the ViewSpace object must be unload calls viewSpaceWasDismissed function in order to remove the modal view and release the previous object.
The problem is that never the retain count reach 0 and the dealloc method of ViewSpace never is called causing memory leaks.
My question is how is possible that a recently created ViewController have 5 retains? I made sure that is never created before.
Thanks.
Cocoa is probably retaining the view controller 4 times internally for reasons of its own. This isn't a problem.
More generally, the -retainCount method is useless for reasons like this, and you should never call it. It will not help you, and it will confuse you.
To debug your leak, I suggest using the leaks Instrument, inspecting the object, and analyzing where each retain and release is coming from to determine whether any are incorrect.
Check the documentation for -retainCount. I believe it says that you should not be calling it yourself - you just need to take care of any retains that you cause, and don't worry about the 'actual' retain count.
You're doing two things wrong here:
The Current view controller retains the modally presented view controller and releaseds it when it is dismissed. So you should release viewSpace after it is presented, and you don't need the release message in the dismissModalViewController method. As an aside ViewSpace is a poor name for a view controller. I had to read to the line where you are presenting it as a view controller before I knew it was a view controller. I think ViewSpaceController is a more descriptive name.
You are using retainCount which is always a bad idea. All that matters is that in your new method you created an owned object (with the alloc) and you balanced that ownership with a release (or at least you will do when you put in the correction I suggested in point 1) That's it. You took ownership of an object and you released it. The retainCount method tells you absolutely nothing that can be of any use to you. Don't do it. Just balance ownerships with release, and that is all that matters.
I'm not 100% sure of every count but here are some:
Instantiation - 1
NIB - 1+
Strong Properties (1+)
Additionally any properties that list it as a strong property (in ARC).
I noticed that when you launch a nib and you use components of the controller in the nib design, it will increase reference counts (in a strong manner) on the controller instance.

What is the difference between dealloc and viewdidunload?

When should I release all the memory I allocated in my program?
Because I only have a viewDidLoad method where I do my business. Should I leave dealloc empty and cleanup only in viewDidUnload?
'dealloc' is used when the object is ready to be freed (i.e., when retain count of the object becomes 0). And viewDidUnload is called when the view is unloaded, but it may not be freed immediately as the reference of the UIViewController is still stored by some other objects.
my personal preference is, for ojbects created by 'init', they are freed by 'dealloc', for objects created by 'viewDidLoad', they are freed by 'viewDidUnload'.
As the documentation of -viewDidUnload says:
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.
There is no mention -viewDidUnload will call in -dealloc, you shouldn't rely on it.

What does 'release' means in this situation on iPhone?

I want to ask a stupid question about the iPhone application. I am the green of the iPhone app. I read the following code in the Apple website.
MyViewController *aViewController = [[MyViewController alloc] initWithNibName:#"MyViewController" bundle:[NSBundle mainBundle]];
[self setMyViewController:aViewController];
[aViewController release];
And I have a question, how it means of the 'release' in the line 3?
Does it presents the memory clear? or the program take control of that object? or other meanings. Thank you very much.
When you alloc something, the object you get will have a retain count of 1 - this means that this object is currently being used by someone, so it should not be removed from memory. If you call retain on an object it will increase the retain count, meaning the object is being used by 2 things. If the retain count reaches 0, it implies that the object is no longer being used by anything and it can be removed from memory. You can decrease an object's retain count by calling release on the object.
In your example, aViewController is alloc'd and after line 1 has a retain count of +1.
It is then set as the view controller in line 2. This method is therefor taking ownership of the object, so should retain it for its own use.
Line 3, we don't want anything more to do with the view controller, so we release our hold of it. The retain count decreases by one - and it is now up to the new owner to release it when it is finished with it.
You might find it helpful to read through the memory management section of this tutrial
Whenever you call alloc, you own a reference to the object that comes back, and you must call release to indicate that you no longer intend to use that reference.
In the above case, you have allocated a new view controller and assigned it to a property of your class. Assuming the property is declared with the retain option, the property will acquire its own reference to the view controller by called retain on it. So there are now two active references to it. The property will eventually release its reference (either when it is assigned a different view controller, or when your class is finalised). But if you don't call release yourself, one reference will remain, and the view controller will never be freed.
In short, you must match every alloc with a release, otherwise things will leak.