So assume I declared an object and created a retained property for it which is synthesized. So something like that in the header file:
NSArray *array;
#property (retain)....
After it is synthesized, I called release in the dealloc.
Now in the init method, if I want to also dynamically allocate that array, what do I do in terms of releasing it? So:
array = [[NSArray alloc] initWithObjects...
How do I keep the object retained as long as the class is running without leaking?
Thank you
self.array = [[[NSArray alloc] initWithObjects:...] autorelease];
or
NSArray *newArray = [[NSArray alloc] initWithObjects:...];
self.array = newArray;
[newArray release];
With both options, you additionally have to call [array release]; in dealloc.
By using its setter method, you normally don't have do worry about retains and releases.
All the init* (init, initWith..., etc.) methods return retained objects. The convenience constructors provided by some classes, on the other hand, provide objects that are not retained - or rather, retained, then autoreleased.
More here.
So you are doing the right thing by assigning a retained object to your ivar in the init method, then releasing it in dealloc.
For the rest of the object's life cycle, it would be smart to only use the synthesized accessors, as they take care of retaining and releasing.
All in all, you're good.
In order to take advantage of the goodness of properties, you need to prefix the variable name with self.
array = [[NSArray alloc] initWithObjects:...];
is not the same as
self.array = [[[NSArray alloc] initWithObjects:...] autorelease];
The former will assign the array directly to the instance variable. The latter will invoke the synthesized setArray method, which gives you retain/release for free. This will be useful should you decide to assign a new reference to array at any other point in time in the lifecycle of your class.
Related
Say I have two properties defined as such:
#property (nonatomic, strong) UITableView *parentTableView;
#property (nonatomic, strong) NSMutableArray *headersArray;
and a method:
-(void)prepareTags;
and say I have an init method like this:
-(id)initWithParentTableView:(UITableView*)parentTable
{
if(self = [super init])
{
//1
NSMutableArray *array = [[NSMutableArray alloc] init];
headersArray = array;
//2
self.parentTableView = parentTable;
//3
[self prepareTags];
}
return self;
}
Is this the correct way to set up the headers array in an init method?
Am I allowed to call self.parentTableView from the init method?
Am I allowed to call a method from the init method (in this case, the prepareTags method calls self too. Will self be ready to use, even though the init method hasn't returned yet?
Respectively (I'd use list formatting but I can't make it work with blockquotes...!):
Is this the correct way to set up the headers array in an init method?
Yes, but there's no point having the array variable, you might as well just do: headersArray = [[NSMutableArray alloc] init];
Am I allowed to call self.parentTableView from the init method?
No, to quote the Apple docs on Practical Memory Management:
Don’t Use Accessor Methods in Initializer Methods and dealloc
. You should access the ivar directly (as you do with headersArray)
Am I allowed to call a method from the init method (in this case, the prepareTags method calls self too. Will self be ready to use, even though the init method hasn't returned yet?
Yes. Just be very careful (your object may not have been fully initialised, it shouldn't use accessor methods so as to comply with the previous restriction, et cetera)
There's no need to use a local variable here:
NSMutableArray *array = [[NSMutableArray alloc] init];
headersArray = array;
Just assign to the instance variable directly:
headersArray = [[NSMutableArray alloc] init];
Am I allowed to call self.parentTableView from the init method?
Yes, although some people might consider that poor design. Consider the fact that properties sometimes have non-obvious complex setter methods that look at other instance variables. Is it wise to do this when your object hasn't been fully initialised?
Am I allowed to call a method from the init method?
Same as above. So long as you aren't relying on anything you haven't initialised yet, it's fine.
The code looks pretty good. Just a couple notes...
-(id)initWithParentTableView:(UITableView*)parentTable
{
// avoid compiler warning about the assignment and the condition in the same statement
self = [super init];
if(self)
{
//1
// no need for the extra stack variable
self.headersArray = [[NSMutableArray alloc] init];
//2
// this is all fine from here
self.parentTableView = parentTable;
//3
[self prepareTags];
}
return self;
}
I have a property in my class, which is an NSArray. I am retaining the property.
My question is, what is the proper way to add objects to that array without leaking and making the retain count too high?
This is what I am using:
.h:
NSArray *foodLocations;
#property (nonatomic, retain) NSArray *foodLocations;
// I make sure to synthesize and release the property in my dealloc.
.m
- (void)viewDidLoad {
[super viewDidLoad];
NSArray *tempFood = [[NSArray alloc] initWithArray:[self returnOtherArray]];
self.foodLocations = tempFood;
[tempFood release];
}
Is this the correct way to do it?
Yes this is correct and my preferred way of doing it as it renders the code more readable.
You are essentially allocating a temporary array and then assigning it to your property with a retain attribute, so it is safe to dealloc it as your property now "owns" it. Just remember that you still need to release it in your dealloc method.
You could also initialise the array and assign it to the property in the view controllers init method, depending on whether you need the property to be available to you before the view actually loads (i.e. in case you want to read the value of the property before pushing the view controller etc...)
you will typically want to declare the property copy in this case.
in most cases, immutable collection accessors should be copy, not retain. a lot of people get this wrong, and end up writing a lot of copying manually and sharing objects which should not be shared, thinking they are doing themselves good by cutting a corner.
copying in this form (the collection) is shallow. the objects in the array are not copied, just the array's allocation.
a good implementation of an immutable collection can simply implement copy by retaining self. if the argument is mutable, you want a copy anyhow (in the majority of cases).
your program is then simplified to a declaration of:
// note: copy, not retain. honor this if you implement the accessors.
#property (nonatomic, copy) NSArray * foodLocations;
and then the setter:
self.foodLocations = [self returnOtherArray];
of course, you must still init, dealloc, and handle thread-safety appropriately.
good luck
That looks fine. You don't actually need the tempFood variable, you can just do:
self.foodLocations = [[NSArray alloc] initWithArray:[self returnOtherArray]];
[self.foodLocations release];
or:
self.foodLocations = [[[NSArray alloc] initWithArray:[self returnOtherArray]] autorelease];
Or:
#synthesize foodLocations=_foodLocations;
then in code
_foodLocations = [[NSArray alloc] initWithArray:someOtherArray];
This avoids the autorelease required by
self.foodLocations = [[[NSArray alloc] initWithArray:someOtherArray] autorelease];
Yes, that is correct. Also good to keep in mind is what #synthesize is, in effect, doing for you. A synthesized (& retained) setter is functionally equivalent to the following code:
- (void)setVar:(id)_var {
[_var retain];
[var release];
var = _var;
[var retain];
[_var release];
}
So, basically, every time you call self.var = foo, it releases the previously stored value and retains the new one. You handle the reference counting in your code, and the setter handles its own.
i have the following code in .h:
#property (nonatomic, retain) NSArray *arrayData;
And in the .m in method initWithNibName:
self.arrayData = [NSArray arrayWithObjects:#"Usuario:",#"Password:",nil];
is it right in order to call
[self.arrayData release]
safely in order to release the object?
No, it is not correct to call release on your property. The problem with it is, that you release your property, it will get deallocated, but you didn't set your pointer to nil, so somebody might send a message to your property and get a crash.
What you can do is the following:
self.arrayData = nil; ( which will release the previous saved instance, and set the property to nil)
[arrayData release]; arrayData = nil; (here you are accessing your ivar instead of your property; setting your ivar to nil is a precaution)
[self->arrayData release]; self->arrayData = nil (this is exactly the same as #2)
Hope this helps.
You need to call:
[arrayData release]
Calling [self.arrayData release]; will not have the effect you want it to in either case.
If you're wondering why this is, check this question out: difference between [self.property release] and [property release]
A)
it is a bad idea to do this in your initializer (e.g., initWithNibName:bundle:)
self.arrayData = [NSArray arrayWithObjects:#"Usuario:",#"Password:",nil];
use this instead:
arrayData = [[NSArray alloc] initWithObjects:#"Usuario:",#"Password:",nil];
you should not call these accessors (properties) in initializers or dealloc.
B)
is it right in order to call
[self.arrayData release]
no. in many cases (assuming you implement some of the properties you've declared), you may not be returned the the ivar. you may receive a copy, a placeholder object, or a subclass may have chosen to re-implement the accessors (as some examples). in these cases, it's easy to over-release or over-retain (resulting in evil stuff, like leaks and crashes).
this is typical:
self.arrayData = nil;
unless you are in dealloc of the object which declared the ivar:
- (void)dealloc {
[arrayData release], arrayData = nil;
[super dealloc];
}
I've read a few posts on this, but there's still one thing that's not clear for me. I know this might be rather a n00b question, but I've actually got rather far into development without quite grasping this fundamental issue. A symptom of being self taught I guess.
You declare a variable in your header, like so:
#interface SomeClass : NSObject {
NSMutableArray *anArray;
}
#property (nonatomic, retain) NSMutableArray *anArray;
end
And then in your main file you synthesise it and set it to an initial value:
#implementation SomeClass
#synthesize anArray
- (SomeClass *)init{
if (self = [super init]) {
self.anArray = [[NSMutableArray alloc] initWithCapacity:10];
}
[return self];
And release it when your Class deallocs:
- (void)dealloc {
[anArray release];
[super dealloc];
}
Now, when I run instruments, the line
self.anArray = [[NSMutableArray alloc] initWithCapacity:10];
is identified as a memory leak. Is it a memory leak because when you define the variable anArray in the header it allocates memory? (Because I thought it was a null pointer.) Therefore when you want to initialise it, and you call [[NSMutableArray alloc] initWithCapacity:10], you are reallocating the memory, and losing the pointer to the original allocation?
So instead, I use the convenience class method:
#implementation SomeClass
#synthesize anArray
- (SomeClass *)init{
if (self = [super init]) {
self.anArray = [NSMutableArray arrayWithCapacity:10];
}
[return self];
This is no longer identified as a memory leak in instruments. And since it's a convenience method, anArray is autoreleased. However, if I am to assume that the instance declaration in the header allocates memory, which would explain the previous issue, then should I still release anArray? Does setting the initial values in this way retain it perhaps?
I understand the difference between
NSMutableArray *anArray = [[NSMutableArray alloc] initWithCapacity:10];
and
NSMutableArray *anArray = [NSMutableArray arrayWithCapactiy:10];
but what I'm not sure I understand is when you've declared NSMutableArray *anArray in your header, which of the two approaches you should use and why. And whether or not if you use the second approach, you should still release anArray when you call dealloc.
I might add that I've found the following posts/links useful:
Suggest the best way of initialization of array ( or other objects )
What is the cost of using autorelease in Cocoa?
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html
What is the difference between class and instance methods?
alloc'ing an object starts it off with a reference count of 1.
Setting a property that has the 'retain' attribute also increases the reference count.
So, that means this is usually bad:
#property (nonatomic, retain) Object * variable;
...
self.variable = [[Object alloc] init];
Because variable now has a reference count of 2.
When setting a object's member variable, just do this:
variable = [[Object alloc] init];
You should also realize that this works
self.anArray = [NSMutableArray arrayWithCapacity:10];
Because "arrayWithCapacity" (and other similar factor methods) autoreleases the object it returns, so after you set the property, it essentially has a reference count of 1.
It's not the instance that allocates the memory. You're right to assume that in Objective-C (at least on all Apple-based operating systems), newly initialized classes have all their ivars set to 0 (or nil or NULL as appropriate).
The problem you're seeing is that you're using the property, not the ivar in your initialization. Since you declared your property as retain, using the property accessor to set it automatically retains it.
So, when you initialize you either have to take ownership and set the ivar directly, or do like you're doing and use the property accessor to set the property and then relinquish ownership in the init method (by either releasing an object you own or, as you did in your second instance, using the convenience constructor so that you never owned the returned instance).
So just remember, if you ever use the property accessors, even within the class itself, you will get the features you set on the property (e.g., nonatomic, retain, etc.). You use the property accessors whenever you do one of the following:
// in these cases the property takes ownership through the
// retain keyword, so you must not take ownership yourself
self.anArray = something;
[self setAnArray:something];
[self setValue:something forKey:#"anArray"];
You would access your ivar directly like:
anArray = something; // in this case you must take ownership
This is what Instruments is pointing to.
students = [[NSMutableArray alloc] initWithArray:[course.students allObjects]];
I'm releasing the array in dealloc. In the rest of my code I'm only calling the array and I'm not alloc'ing it again. I've also tried filling the array via fast enumeration and I get the same problem.
Just to be sure, add an autorelease to it, like
students = [[[NSMutableArray alloc] initWithArray:[course.students allObjects]] autorelease];
Then see what happens. (maybe assign it to self.students btw, and make that a retained property using #property (nonatomic,retain))