Is it a leak if I have a view controller and allocate the view like this:
self.view = [[UIView alloc] initWithFrame:frame];
Do I need to do something like this:
UIView *v = [[UIView alloc] initWithFrame:frame];
self.view = v;
[v release];
Yes, the second one. Properties (self.view) retain their value usually.
Yes, it's a leak. Your solution is correct, or you can do:
view = [[UIView alloc] initWithFrame:frame];
Where view is an instance variable. But it's not such good practice. As commented below, in a UIViewController view is a superclass property, so my code example is doubly wrong. However, the principle that self.variable is invoking setVariable:, and will observe the retain style of the property declaration is worth noting. In such cases you can directly assign to the instance variable above, which omits the retain - and makes such code a horror to maintain, which explains why Apple's Objective C 2.0 property syntactic sugar isn't universally admired.
Corrected because Georg was entirely correct.
This depends on the declaration of the view property. If it's not a (retain) property, then you're fine. If it is a retaining property, you must call release.
You need to read the Cocoa Memory Management Rules.
You obtained the object with +alloc. Therefore, according to the rules, you are responsible for releasing it. Your own solution is perfectly fine, or you could do:
self.view = [[[UIView alloc] initWithFrame:frame] autorelease];
There is a very simple rule for Objective-C memory management. If you've sent retain message, you've to send release also. I do not know exceptions with this rule is SDK itself.
Here we've alloc, that sends retain itself (always), so you have to release object somewhere. You can do it in dealloc or right here, after assigning it to self.view.
In objective c, we need to maintain retain count of an object as zero after its purpose. So here in your code you must release the object. Then it will not create any leakage problems.
The second one you specified is the right way. It is not a compulsory , it is the way to express how effective our code is.
Related
This may sound a newbie question, however I'm new to iOS dev.
Suppose with have this code.
UILabel* label = [[UILabel alloc] init];
...
[someScrollView addSubview:label];
...
label.text = #"Some Text";
is it good practice to modify the view after addSubview ?
Actually my concern is following probably, it is possible that label get released before reaching to label.text assignment, for instance in viewDidUnload, right ? and the assignment will fail.
Overall my questions are
it is good practice to modify views after addSubview ?
is it good practice to release view after addSubview, and later if I need to get any subview to look for it using following technique for (UIView *view in self.subviews) { if (...) ... } ?
It is fine to change properties of a view after you add it as a subview. Those properties will be applied (or animated) on the next turn of the runloop when UIKit renders stuff.
You should absolutely release your view after adding it as a subview IF you no longer need to own it. In other words, follow the memory management guidelines for all cocoa programming. Doing addSubview will cause the owing view to retain it (since it needs it). If you need to change a property on the view in the future though, you should retain it so you have access to it
Your code is fine as long as it is all in the same method and label is not reassigned during any of the ... sections.
Modifying a view before or after adding it to a subview makes no difference.
If you have allocated a view, then added it to a subview, and you don't wish to keep a separate reference to it, you should release it - this is standard memory management. The super view will retain its subviews.
To get hold of a reference to your subview again, your two options are:
Set the tag on the subview before adding it, then use viewWithTag: to get it later
Keep a reference to the subview as an instance variable (in this case, you wouldn't be releasing it after creating it, you'd release it on dealloc).
I’ve used both ways - modifying before and after and have not had any issues either way. The superview is retaining the subview, so if you don’t release the superview or set the subview to NIL somewhere, you’re pretty safe.
Yes, you need to release the view you added after addSubview, but easiest to do it like this:
UILabel* label = [[[UILabel alloc] init] autorelease];
Then it will be released automatically and you don’t have to worry about explicitly releasing it.
I've been teaching myself Objective C recently, and have noticed the following pattern used a lot in tutorials and sample code (including samples from the Apple site).
UIView *myUiView = [[UIView alloc] init];
self.uiView = myUiView;
[myUiView release];
I was just wondering though, it seems a bit of a waste to create a new variable, just to set a property. I've also seen the following pattern used too, but from what I understand its considered bad form to use autorelease on an iOS device as the autorelease pool takes up quite a bit of overhead which might not be good on a mobile device
self.uiView = [[[UIView alloc] init] autorelease];
I've been toying with using the following pattern recently, which sets the property, and then calls release on the property (to decrease the reference counter on the property itself).
self.uiView = [[UIView alloc] init];
[self.uiView release];
I've managed to use it on a few ViewControllers with no ill effects, but is this valid code, or am I missing something which makes it a bad idea?
The property getter is a method, and it does not have to return an ivar, it may actually get its return value anywhere, so you could release that, but it could be an autoreleased value already. If that is the case, you're in trouble.
IOW, if a property getter would do something like (not usual, but possible and valid):
- (NSString *) helloString
{
return [[myStringIVar copy] autorelease];
}
and you do:
[self.helloString release];
then you failed in two ways:
You did not release the ivar you wanted to release
You release an autoreleased object
IMO, it is better to release the ivar directly:
[myStringIVar release];
If the implementation of the property getter is simply to return the reference to the underlying ivar, then it is perfectly equivalent and you simply decrease the retain count of the allocated object.
On the other hand, if you can't be sure what the getter does (what if it returns something else than the ivar, e.g. some calculated result etc.), it may be dangerous.
No. It's not valid.
It will probably work on most retain properties but not necessarily. It will probably break on copy properties and assign properties.
Properties are just a pair of methods, one of which sets an abstract entity and one which gets it. There is absolutely no guarantee in general that the getter will give you the exact same object that you just passed to the setter. For instance, if you pass a mutable string to an NSString copy property, you definitely won't get back the same object.
Use either of the first two patterns. The first one does not waste anything. It is likely the local variable will only ever exist in a register. The overhead of the second will only last as long as the next autorelease pool drain and is only a few bytes (bear in mind that the actual object will last as long as self in any case).
It's not valid, and even in the cases where it does work its a bit "ugly" the other two are just to fix the property's retain characteristic from making the retain count 2 after having an alloc already making the retain count 1.
I tend to do what you described in the first example or the following.
in my #interface
#property (nonatomic, retain) UIView *uiView;
in my #implementation
#synthesize uiView = _uiView;
then when I setup the property.
_uiView = [[UIView alloc] init];
Thinking in terms of the reference counter, nothing is wrong with calling release using the property value. However, there are a few things, which I (personally) would dislike:
The property syntax is really just syntactic sugar for method calls. So, what your code really looks like, is
[self setUiView: [[UIView alloc] init]];
[[self uiView] release];
Another thing here might be more due to me thinking in strange ways, but I like to think of the reference counts as actually being related to references. A local variable holding a pointer to my object is such a reference. Having it present in the code reminds me, that I have something to do in order to clean things up properly (or, if not, at least write a short comment, why I don't have to clean up).
Directly going through the property forces me to think in terms of reference counts instead, which I don't like.
I'm currently working through Apress's "Beginning iPhone 3 Development". A standard they use in their example applications is like the following code:
- (void)viewDidLoad {
BlueViewController *blueController = [[BlueViewController alloc]
initWithNibName:#"BlueView" bundle:nil];
self.blueViewController = blueController;
[self.view insertSubview:blueController.view atIndex:0];
[blueController release];
}
8.14.11 UPDATE (additional information)
blueViewController is declared as follows:
#property (retain, nonatomic) BlueViewController *blueViewController;
Whenever they perform an alloc they put it in some temp variable (here blueController) then they assign it, then they release it. This temp variable seems superfluous to me.
I simplified the code as follows:
- (void)viewDidLoad {
self.blueViewController = [[BlueViewController alloc]
initWithNibName:#"BlueView" bundle:nil];
[self.view insertSubview:blueViewController.view atIndex:0];
}
- (void)dealloc {
[blueViewController release];
[super dealloc];
}
My modified code ran just the same in the iPhone simulator.
Now, I know the rule that if you alloc something you need to release it. And I'm covering that in my dealloc method. But is there some advantage to having a release directly in the ViewDidLoad (the function where the alloc was called)? Or is it equally ok to have a release in your dealloc method like this?
Thanks for any help,
-j
Assuming blueViewController is a retain property, the temporary variable is not superfluous. Your simplification is creating a memory leak. This statement from the second snippet leaks:
self.blueViewController = [[BlueViewController alloc]
initWithNibName:#"BlueView" bundle:nil];
In terms of ownership, you own the object returned by alloc-init and then the property accessor claims ownership of the object again, resulting in the object being over-retained.
Using a temporary variable solves this problem. Another option is to use autorelease:
self.blueViewController = [[[BlueViewController alloc]
initWithNibName:#"BlueView" bundle:nil] autorelease];
Note that after this statement you effectively own the object and you must release it in dealloc.
You did not mention how the property blueViewController is declared. Anyway, whatever the semantics of the setter are (retain, copy, assign), that statement is wrong. I already explained the most likely scenario: retain. Let's have a look at the other two possibilites (without considering if they make sense at all):
If blueViewController happened to be a copy property, the statement would leak too. The property accessor copies the original object and now the property holds a pointer to the copy and you lost track of the original object, immediately leaking it.
The least likely scenario is that blueViewController is an assign property because this is most likely wrong and you really want retain. But, anyway, the assign properties are for objects you do not own, e.g. delegates, and you are not supposed to release them. You are assigning an object you own to it, so either you leak it or you incorrectly release the assign property.
#property (retain) MyCLass *obj;
MyClass *tmpObj = [[MyClass alloc] init];
self.obj = tmpObj;
[tmpObj release];
In the first line you get ownership once via alloc. Then in 2nd line you get ownership again as the property is retained. In 3rd line you release the ownership that you got via alloc. Now you have a single ownership via retain property which you may release in future, may be in dealloc.
Now consider what happens if you remove the tmpObj.
self.obj = [[MyClass alloc] init];
In this line you get ownership twice, once via alloc and once via property. Now [obj release] once is not enough. You need to release it twice to avoid the leak, and naturally releasing twice is extremely bad and possible source to further memory leak. If you make another call to self.obj = anotherObj then you are leaking the old one. To avoid this you need this temporary pointer.
There's two reasons I can think of off the top of my head; the first, more specific to the example, is that you will often see similar methods where blueController is allocated and initialized, then tested for validity before actually being assigned to self's ivar. Following this pattern in every such method will make it easier for you to do tests in between creating the object and assigning it, should you realize that needs to happen. To my knowledge, if such an intermediary indeed remains superfluous, the compiler should optimize it out.
The second, more general purpose for this pattern within Cocoa is simply that Obj-C and Cocoa encourage extremely long, verbose names for methods and variables, so a single method call could end up spanning multiple lines; using method calls as direct arguments for other methods can quickly become unreadable, so conventions encourage setting up each argument for a method ahead of time, placing them in intermediary variables, then using the variables as arguments to enhance readability, and make it easier to change a single argument without having to dig around nested method calls.
does this retain my subview twice?
- (void)viewDidLoad
{
CGRect frame=CGRectMake(0, 0, 320, 460);
mapButtons*newButtons=[[mapButtons alloc] initWithFrame:frame];
self.mapButtons=newButtons;
[newButtons release];
[self.view addSubview:self.mapButtons];
[self.mapButtons addButtons:#"yo"];
once it is added to the view hierarchy with addSubview, does it get an additional retain count beyond that retained by the ivar, self.mapButtons?
i want to be able to manipulate this subview easily, hence the ivar; is this a good way, or is there a better way?
EDIT
You mention memory-wise so I think it may need some clearing up. Each object has a retain count which is incremented with retain and decremented with release. When the retain count reaches 0 a dealloc message is sent. So when you put an additional retain on an object you are not using anymore memory you are simply incrementing the counter and not doing any kind of duplication.
There are a couple of ways you can grab a reference to a view but the way you are doing it is a good way. An alternative would be to tag the view and retrieve it from self.view using
UIView *view = [self.view viewWithTag:tagId];
I prefer the ivar way e.g. how you have done it (this will change when ARC comes in) but I tend not to worry about the the actual retain count of an object. I concentrate on balancing my retain/releases.
Therefore I use the rule that if it's a local variable I try as far as possible to match my retain/release's within the scope it is defined in. The exception being ivar which are released in dealloc
In Xcode I have a UINavigationController on wichh I push a UIViewController.
In that UIViewController I init an UIScrollView.
All good.
However, after switching to another view I log the retain count of the controller and to my surprise it's one instead of zero.
Here's the code:
scroller = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
[self.view addSubview:scroller];
and:
- (void)dealloc {
[super dealloc];
NSLog(#"retaincount, %d", [scroller retainCount]); //displays 2
[scroller release];
NSLog(#"retaincount, %d", [scroller retainCount]); // displays 1
}
I only init it ones and add it to the UIViewControllers view.
Kind regards,
Tom
Do not use retainCount! From the apple documentation:
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).
Having said that: You have to call [super dealloc] in the very last line of your dealloc method. Besides that everything in your code should be ok. Don't try to manually lower the retainCount. Use proper memory management. Again, do not look at the retainCount.
And you are not allowed to use an object after you have released it. If the scroller would be deallocated because of your release the second NSLog would result in a BAD_ACCESS exception.
self.view is retaining it. When your UIViewController deallocs, it'll release self.view, which will release all its retained subviews. In fact, you should probably release it soon after adding it to self.view.
That said, I strongly second #fluchtpunkt's answer. Examining the retainCount property looking for debugging info will only lead to confusion and increasingly coherent and ranty posts on Stack Overflow.
This is why:
scroller = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
//retain count = 1
[self.view addSubview:scroller];
//retain count + 1 + 1 = 3
//later the AutoreleasePool decrements the count by 1, resulting in a retain count of 2.
Methods beginning with init…
return non-autoreleased instances
with a retain count of 1.
Accessing your
subview via self.view: returnes an autoreleased retained pointer to subview.
Adding a
subview to a view via addSubview: retains the
subview.
Removing a subview from its superview via removeFromSuperview: releases it.
You should not rely on an object's retain count though.
Instead you must take care to level out retain and release calls on an object.
Make sure to read Apple's Memory Management Programming Guide in this matter! (the rules section in particular!)
Previous answers are correct: don't use -retainCount, and make sure that you call [super dealloc] last in your own -dealloc implementation.
I want to add that you'll probably never see a case (not that you should be looking) where -retainCount returns 0. If an object's retainCount drops to zero, the object is deallocated. In fact, it seems that Cocoa never even bothers to set the retainCount to zero... -release appears to deallocate the object if the previous retainCount was 1. Just one more reason to consider -retainCount to be a private implementation detail.