How to properly manage id instance variable assign then retain - objective-c

I have a class that has a property:
#property (nonatomic, assign) id customDatePicker
Based upon user choice I will need a UIDatePicker or a UIPicker so I typecast the ivar and retain it and subsequently dealloc it. Is this the correct way to manage the property's memory?
// init snippet
if (useUIDatePicker) {
customDatePicker = (UIDatePicker *)[[[UIDatePicker alloc] initWithFrame:frame] retain];
} else {
customDatePicker = (UIPickerView *)[[[UIPickerView alloc] initWithFrame:frame] retain];
}
- (void)dealloc {
[customDatePicker release];
[super dealloc];
}

No.
When you declare the property as assign, you should not be retaining the object. The assign option is used for non-object variables and for situations where having a retained property would create a cycle, with both objects retaining each other. Declaring a property as assign means you will not be managing the memory of the object; you should neither call retain nor release on it.
You are also over-retaining the picker object. Retaining an object creates a claim on the object; you don't want it to disappear until you say you are done with it. You relinquish a claim, allowing an object to be deleted, by calling release. When you call alloc, that creates the same kind of claim as calling retain. So this line:
[[[UIDatePicker alloc] initWithFrame:frame] retain];
creates two claims, one for alloc and one for retain. Later, you only call release once, which means that you will always still have one claim on this object, and it will have turned into a memory leak.
What you should do is:
#property (nonatomic, retain) id customDatePicker
if (useUIDatePicker) {
customDatePicker = [[UIDatePicker alloc] initWithFrame:frame];
} else {
customDatePicker = [[UIPickerView alloc] initWithFrame:frame];
}
Now you have only one claim on the picker because you used alloc.
(You don't need to cast the assignment; when you are assign to a generic pointer you can use any kind of object.)
Also take a look at the Apple Memory Management docs.

Related

I wonder about releasing variables

UIView *view; //1
UISegmentedControl *scopeBar; //2
NSMutableArray *array; //3
#property (nonatomic, retain) IBOutlet UIView *view;
#property (nonatomic, retain) UISegmentedControl *scopeBar;
#property (nonatomic, retain) NSMutableArray *array;
.m
#synthesize view, scopeBar, array;
for (id subView in [view subviews]) {
if ([subView isMemberOfClass:[UISegmentedControl class]]) {
scopeBar = (UISegmentedControl *)subView;
}
}
array = [[NSMutableArray alloc] init];
- (void)dealloc {
}
I think that only the third of the variables has to be released in the dealloc method.
Is that right?
Yes, (array needs to be released) because you alloc it. So, it's programmer's responsibility to release it. So -
- (void)dealloc {
[ array release ] ;
// Any other resources alloc, init, new should be released
}
For more info on what to release, Memory management - ObjectiveC
And I think you will find good suggestions in this question about your query
Why should we release?
Contrary to some of the answers, you have to release your outlet (view) as well, and not only in the dealloc but also in the viewDidUnload, the easiest way is to set it to nil :
self.view = nil;
Also note that if you don't access your properties but your instance variables (i.e. without self. prefix) your retain attribute won't help you and you are not retaining the object. That means that as soon as scopeBar would be removed out of the subViews of the view, it will be released and you end up accessing a zombie.
As a rule of thumb, it's best to use the properties accessor everywhere except in the init methods so that you don't have to deal with the memory management explicitly. Setting them to nil in the dealloc and viewDidUnload in case of outlets should be enough then.
Also, don't do what Jenifer suggested and once you've called a release on a variable, don't set the property to nil, that would overrelease it.
I think that only the third of the variables has to be released in the dealloc method. Is that right?
// no. your dealloc should look like this:
- (void)dealloc {
// note: *not* using accessors in dealloc
[view release], view = nil;
[scopeBar release], scopeBar = nil;
[array release], array = nil;
[super dealloc];
}
// your assignment of `scopeBar` should look like this:
...
self.scopeBar = (UISegmentedControl *)subView;
...
// you want to retain the view, as advertised.
// consider avoiding an ivar if you can easily access it.
// your assignment of `view` should look like this:
...
self.view = theView;
...
// you want to retain the view, as advertised.
// consider avoiding an ivar if you can easily access it.
// your assignment of `array` should look like this in your initializer:
// note: *not* using accessors in initializer
...
// identical to `array = [[NSMutableArray alloc] init];`
array = [NSMutableArray new];
...
// and the assignment of `array` should look like this in other areas:
...
self.array = [NSMutableArray array];
...
// you're likely to be best suited to declare your array as
// follows (assuming you really need a mutable array):
...
NSMutableArray *array; // << the declaration of the ivar
...
...
// the declaration of the public accessors.
// note the array is copied, and passed/returned as NSArray
#property (nonatomic, copy) NSArray *array;
...
// finally, the implementation manual of the properties:
- (NSArray *)array {
// copy+autorelease is optional, but a good safety measure
return [[array copy] autorelease];
}
- (void)setArray:(NSArray *)arg {
NSMutableArray * cp = [arg mutableCopy];
// lock? notify?
NSMutableArray * prev = array;
array = cp;
[prev release], prev = nil;
// unlock? notify? update?
}
other answers assume that dangling pointers (e.g., you still hold a pointer to view, although the view may have changed behind your back) are allowable.
they should not be allowed in real programs. they are extremely dangerous, and it can very difficult to reproduce errors they cause. therefore, you must ensure you own a reference to the pointers you maintain/hold.
you should also use the accessors in the public interface for the subclasser's sake - in case they override them. if you don't want to allow/support that, consider simply using a private variable.
As i think you should release and set them nil because you have made them properties so do this:-
in your dealloc
[array release];
self.array=nil;
self.scopeBar=nil;
self.view=nil;

Methods from #synthesize?

When you synthesize a property (see below)
#interface CelestialBody : NSObject {
NSString *name;
}
...
#interface Planet : NSObject {
NSString *name;
int mass;
CelestialBody *moon;
}
#property(nonatomic, retain) NSString *name;
#property(assign) int *mass;
#property(nonatomic, retain) CelestialBody *moon;
...
#implementation Planet
#synthesize name;
#synthesize mass;
#synthesize moon;
...
You get setters and getters for each of the iVars (i.e.)
[newPlanet setName:#"Jupiter"];
[newPlanet setMass:57];
NSString *closestName = [newPlanet name];
int largestMass = [newPlanet mass];
CelestialBody *newMoon = [[CelestialBody alloc] initWithName:#"Callisto"];
[self setMoon:newMoon];
[newMoon release];
but you also get the ability to release the object using ...
// Releases the object (frees memory) and sets the object pointer to nil.
[self setMoon: nil];
There will of course be deallocs for each Class.
// Moon
-(void)dealloc {
[name release];
[super dealloc];
}
// Planet
-(void)dealloc {
[name release];
[moon release];
[super dealloc];
}
Am I getting this right?
gary
Unless your planet object is declared as a property within some other class, using the retain/copy attributes, you can't release it this way.
When you declare a property using retain/copy, the resulting setter will release the old value and assign the new value, retaining or copying it in the process. If you pass nil, you will release the old value and assign nil, retaining or copying it, and retaining/copying nil is nil, so effectively you end up releasing the old value and assigning nil to the ivar.
This is an acceptable way to release instance variables.
In order to be able to release your newPlanet instance this way, you'd have to have declared it in a class as a property with either retain or copy.
As a further example, since your planet object declares its properties in this way, you could release those using this method.
Or in the Planet class's dealloc method, you could do:
self.name = nil;
This would release name and assign nil to it.
"you also get the ability to release the object"
Yes, as long as you didn't declare it with the assign attribute.
As you probably know, one of the reasons (although perhaps not the primary one) for using declared properties is that you can do:
self.moon = aMoon;
rather than;
[self setMoon:aMoon];
They are equivalent. That means that your deallocation can look like this:
self.moon = nil; // Releases and sets to nil
But remember to never just do:
moon = nil; // Sets to nil, doesn't release
It's very good practice to not only release the object, but to set the variable to nil, as you do, because otherwise some other code could mistakenly try to use the pointer that is left in the variable.
Your example shows the synthesis of one class's ivars (those of Planet) but the use of another (whatever "self" is). Is the "newPlanet" property of "self" in your last example also synthesized as (retain)? If so, then: Yes, setting newPlanet to nil will release whatever self's old "newPlanet" was.
I think you are not getting it right.
After your question update, yes, you can do that, and also:
self.moon = [[CelestialBody alloc] initWithName:#"Callisto"];
and release it later, probably in your dealloc method:
self.moon = nil;
Apple Objective-c 2.0 Properties and Memory Management docs are pretty good. Check Mac Dev Center library.

'initializing' a property which is retained

In the iPhone objective-c world, I've seen this pattern everywhere and I use it myself all the time without really understanding what is going on:
In Test.h
#interface Test: UIViewController
{
NSMutableArray *testArray;
}
#property (retain, nonatomic) NSMutableArray *testArray;
And in Test.m
#implementation Test
#synthesize testArray
- (void) viewDidLoad
{
// why do we do this?
NSMutableArray *init = [[NSMutableArray alloc] init];
self.testArray = init;
[init release];
[self.testArray addObject: #"A"]; // why can't I do this directly?
...
}
- (void) dealloc
{
[testArray release];
[super dealloc];
}
My question is: if testArray has a retain on it when it's declared in the property, why do we need to create a new NSMutableArray init object, assign that to testArray and release? Why can't I just start using testArray in viewDidLoad without doing anything else?
I know there's some debate over the best way of doing this (creating a new object, or using an autorelease object), but in both cases, we end up with testArray with a retain count of 1. Which I believe the 'retain' property already gives it. So why the need to create this init object?
The 'retain' property doesn't automatically create an NSMutableArray for you. Rather, it simply indicates that whenever you do assign something to that property, it will be retained.
If your code were this:
- (void) viewDidLoad
{
[self.testArray addObject: #"A"];
}
Then self.testArray would be nil, and thus it would be essentially a no-op. Until you assign something to self.testArray, it's empty.
Here's what's going on.
- (void) viewDidLoad
{
// we need to assign an NSMutableArray to self.testArray.
NSMutableArray *init = [[NSMutableArray alloc] init];
// The array has been retained once (by the call to |alloc|)
self.testArray = init;
// The array is assigned to a property with the 'retain' attribute
// Thus, the array has now been retained twice
[init release];
// We release the array, so it now is retained once.
// We now have an array in self.testArray, so we can add something to it.
[self.testArray addObject: #"A"];
}
The "retain" in the #property directive specifies that the setter should retain the input value instead of simply copying the value. It has nothing to do with allocating (setting aside memory) and initializing (constructing the object) the object. retain on the #property directive simply increments the retain count when the setter is called (which alllows you to do something like self.myobject = something without specifically calling retain.

Should I release this property?

I'm a objective c newbie, and i'm having a bit of problems with memory management, I've read the apple's memory management policies, however i need a bit of clarification here, this is pretty simple i guess, but i would like to ask you if I'm right:
Given this property:
#interface Test : NSObject {
NSArray *property1;
}
#property (nonatomic,retain) NSArray* property1;
#end
...
//And its implementation:
#implementation Test
#synthetize property1;
-(id) init {
if (self=[super init]) {
self.property1=[[[NSArray alloc] initWithCapacity:5] autorelease];
}
return self;
}
-(void) dealloc {
[super dealloc];
[property1 release];
}
#end
Is it right to issue an Autorelease message to the allocated object in the init method?, i do this cause in apple's document, says that every allocated object should be released by the developer, then, I think, alloc sets retain count to 1, then the property (nonatomic, retain) adds 1, so retain==2, then autorelease substracts 1, and when the dealloc method is called, property1 is released and retain count==0, am I right?
You have your memory management right, though Apple (and a lot of other people) generally recommend not using accessors in your initialization methods because accessors can have side effects beyond simply setting an instance variable that your class might not be set up to handle yet. And in that case, you wouldn't want to autorelease since you'd want ownership of the object.
one side note: in your dealloc, you need to release the property before calling [super dealloc], because [super dealloc] eventually deallocates the memory of the object, which includes the memory containing the property1 variable, so it is invalid to refer to that variable after you call [super dealloc]. It should be:
-(void) dealloc {
[property1 release];
[super dealloc];
}
One of the nice things about using properties is that you can encapsulate all of your "releasing" behavior regardless of whether your property is set to retain, copy, assign, or whatever by just doing this:
self.property1 = nil;
Personally I've gotten in the habit of setting all properties to nil (using self.property, not just accessing the member variable directly) in dealloc so that even if I change how the memory management works for the member variable it works correctly.

Property attribute "retain" doesn't seem to be working?

I've implemented a bit of code from one of the many Apple code examples, but I'm having a bit of trouble, because the retain attribute of one of the properties doesn't appear to be working. Here's the property declaration:
#property (nonatomic, retain) EditingViewController *editingViewController;
And here's the code:
- (EditingViewController *)editingViewController {
// Instantiate the editing view controller if necessary.
if (editingViewController == nil) {
EditingViewController *aController = [[EditingViewController alloc] init];
editingViewController = aController;
[aController release];
}
return editingViewController;
}
I understand that (retain) is supposed to cause the retain count to increase by 1 on assignment; however, the code fails unless I do send [aController retain] myself, or don't send [aController release]. What am I missing here?
When you reference editingViewController, it is equivalent to self->editingViewController, i.e. an access to an ivar.
If you want to use a getter or setter, you need to use self.editingViewController, or equivalently [self setEditingViewController:aController].
This is why I prefer to use an ivar with a different name to the property, for example:
EditingViewController* i_editingViewController;
#property (nonatomic, retain) EditingViewController *editingViewController;
#synthesize editingViewController = i_editingViewController;
Then you can write your lazy getter as:
- (EditingViewController *)editingViewController {
// Instantiate the editing view controller if necessary.
if (i_editingViewController == nil) {
i_editingViewController = [[EditingViewController alloc] init];
}
return i_editingViewController;
}
or
- (EditingViewController *)editingViewController {
// Instantiate the editing view controller if necessary.
if (i_editingViewController == nil) {
EditingViewController *aController = [[EditingViewController alloc] init];
self.editingViewController = aController;
[aController release];
}
return i_editingViewController;
}
I would probably use the former method (not invoking the setter) because the value of editingViewController (as seen by any observer) has not really changed, but either way should work fine and the different name (for ivar and property) help avoid the confusion or accidental misused. It is also a mild encouragement to use the property (since it avoids the slightly ugly prefix).
Note that Apple reserves the _ prefix, and that setters and getters should not be used in the init/dealloc routines.
You have to write self.editingViewController in order to use the property. Just "editingViewController" is a direct access to the Class member variable, whereas self.editingViewController is equivalent to [self setEditingViewController:...] and will do the appropriate retain/release job.