objective c memory leak with array? - objective-c

In my application i declare an array property,
#property (nonatomic, retain) NSArray *listOfItems;
and in my viewDidLoad method,
listOfItems = [[NSArray alloc] initWithObjects:#"First", #"Second", nil];
I do not release the array in my viewDidLoad, because the objects in the array will be required elsewhere in the application.
Finally in my dealloc method i put,
[listOfItems release];
My question is: Is there a memory leak in this code? The retain count should be increased twice due to the (retain) in the property as well as the alloc in the viewDidLoad, but only decreased once in the dealloc method.

the retain will only 'kick in' when you do it like this
self.listOfItems = [[NSArray alloc] initWithObjects:...];
Now, the retain count is indeed 2. If you leave self out, it will just be one. There is a distinct difference in calling 'set' and just assigning.
To answer your original question; your code is not leaking.

Since your setter retains the array, you must release it, after you allocated and set it in your viewDidLoad method (with this notation I suggest "autorelease").
But perhaps it's easier for you, if you use [NSArray arrayWitObjects:].

NSArray *array=[[NSArray alloc] initWithObjects:#"First",#"Second",nil];
self.listOfItems=array;
[array release];
You, can use this way also.

Just like #MiRAGe said,
listOfItems = [[NSArray alloc] initWithObjects:#"First", #"Second", nil];
The retain count will be 1
but if the code is
self.listOfItems = [[NSArray alloc] initWithObjects:#"First", #"Second", nil];
The retain count will be 2

Yes, there's a potential leak. Consider:
In -viewDidLoad, you set up listOfItems as you've described.
Subsequently, some other code calls yourObject.listOfItems = someNewList;.
Your -dealloc releases listOfItems.
I realize that #2 above probably doesn't happen in your code, but a month from now you may decide that you need to update the list for some reason and introduce this problem. Think about what happens if #2 ever does occur...
Your listOfItems ivar suddenly points
to a different list.
You no longer have a reference to the old list, so it can never be released. That's your leak.
It's much better to release the array in -viewDidLoad after you've assigned it to your property.

It wont be confusing if you practice like this
#property (nonatomic, retain) NSArray *listOfItems;
NSArray *temparray = [[NSArray alloc] initWithObjects:#"First", #"Second", nil];
self.listOfItems = temparray;
[temparray release];
and in dealloc
[listOfItems release];

Related

When to use Self?

I am new to iOS development.
I have property as follows,
#property(nonatomic,retain)NSMutableArray *dataArray;
I am doing the following, to alloc it
self.dataArray=[[NSMutable alloc]init];
In the dealloc I am doing the following
-(void)delloc{
[dataArray release];
[super dealloc];
}
But I am getting memory leak for my array initialization.However , it doesn't create the
leak when I don't use self. But I wonder is it a write approach to initialise the array
without using self. Any help is appreciated.
You're getting a leak because the dataArray property is declared with retain, which means that when you use self (thus you use the setter), your retain count goes up to 2 and you only release it once. On the other hand, if you only use the ivar, the retain count is 1 (because of alloc) and you release it once, which is fine. To avoid the memory leak in the first situation, autorelease it like this.
self.data = [[NSMutableArray alloc] init] autorelease];
That will balance the retain count. As for access, except for inside the dealloc method, try to use self (setter and getter)
You should read the memory management docs, first thing to start with when developing for Cocoa Touch.
Also, why don't you use ARC?
If you use the self. signature you are accessing to the object via automatically generated / custom getter/setter. The setter will tipically manage the memory and you don't need to do that.
If you don't use self you access directly to the object.
The code what you presented is leaking, because the default setter of the dataArray will retain to the array, what you set with self.dataArray = [[NSMutableArray alloc] init];
The correct usage is:
self.dataArray = [[[NSMutableArray alloc] init] autorelease];
or:
_dataArray = [[NSMutableArray alloc] init];
What's happening here is that alloc is adding one to the retain count of the new object. The property reference is also retaining the object. If you want to do it this way, you only want one of those. A common method is:
self.dataArray = [[[NSMutableArray alloc]init] autorelease];
However, better still is to use ARC as #c.cam108 suggested and avoid the whole problem.

Regarding memory management in Objective C

According to the static analyzer if we have the following property:
#property (retain, nonatomic) SomeObject * object;
and then we assign the property like so:
self.object = [SomeObject alloc] init];
a leak occurs. This makes sense because the alloc init adds +1 to the retain count and then the retaining property also increments the retain count. What is the best solution here? typically I just add an autorelease like so:
self.object = [[SomeObject alloc] init] autorelease];
But sometimes this creates problems for me and I end up over releasing the object causing my app to crash. I don't have any specific examples right now but I remember I had to take out some autoreleases cause of the application crashing. Is there something I am missing here?
EDIT: I have a concrete example now of the issue I was running into.
NSMutableArray *newData = [NSMutableArray array];
//If this is true then we are showing all of the items in that level of hierarchy and do not need to show the summary button.
if (!(contextID.count >= 1 && [[contextID objectAtIndex:contextID.count - 1] isEqual:[NSNull null]]) && contextID.count != 0)
{
GeographyPickerItem * firstItem = [[GeographyPickerItem alloc] init];
firstItem.primaryString = [NSString stringWithString:#"Summary"];
firstItem.subString = [NSString stringWithString:#""];
firstItem.isSummaryItem = YES;
[newData addObject:firstItem];
[firstItem release]; //TODO: Figure out why this is causing EXC_BAD_ACCESS errors
}
self.hierData = newData;
The code above is in the init method of a viewcontroller. HierData is a retained property, which is released in the viewControllers dealloc method. GeographyPickerItem retains the two strings, primaryString and subString and releases them in its own dealloc method. My application crashes (sometimes) when the viewControllers are de-alloced following a pop off of a navigation controller. It crashes with a EXC_BAD_ACCESS signal in the dealloc method of GeographyPickerItem (either on [substring release] or [primaryString release]).
I don't understand why this is happening because I believe I am following proper memory management guidelines. If I comment out firstItem release everything is fine.
The autorelease method you mention is fine, as is the other common idiom of:
SomeObject *thing = [[SomeObject alloc] init];
self.object = thing;
[thing release];
If you end up overreleasing later on, that is your problem. This part, which you're apparently doing correctly, is not the problem.
SomeObject * new_object = [SomeObject alloc] init];
self.object = new_object;
[new_object release];
or use ARC
check the GeographyPickerItem, if the strings properties are assign (and change to retain), or check if you always initialize them (before release).
also remember the difference of manually allocating :
[[NSString alloc] initWith...]
You must release or autorelease.
[NSString stringWith...]
No need to release.
or use ARC like meggar said
Turns out the issue was simple, my dealloc method called super dealloc at the start of the method rather than at the end. You always have to release your instance variables before you call [super dealloc]!

NSMutableArray becomes empty when I call any IBAction

Please help me i have a NSMutableArray which contains data until viewDidLoad is finished.
When I click any button I try to get the data in it and it just disappears.
If you created your array with a variant of [NSMutableArray array] it will be autoreleased. Assuming myArray is a property of your class, you should use
myArray = [[NSMutableArray] alloc] init]; //or initWithCapacity, etc.
If you still use reference counting, you need to add [myArray retain]; unless it is already retained in the #property declaration and release it in viewDidUnload:.

objective c setter method memory management

I have question about allocating and releasing objects:
if I have code like this:
MyObject *object = [[MyObject alloc] init];
NSMutableString *string = [[NSMutableString alloc]initWithString:#"bla"];
object.myString = string;
NSLog(#"retain count: %d",[object.myString retainCount]); //gives me 2
[string release];
NSLog(#"retain count: %d",[object.myString retainCount]); //gives me 1
Than I have exactly what I want. I need just one reference and I have retain count 1
but
if I use
object.myString = [[NSMutableString alloc]initWithString:#"bla"];
my property look like this:
#property (nonatomic,retain) NSMutableString *myString;
one alloc, and one setter method with retain gives me as retain count 2
If I release the object after resignment than the app crashes. I dont know why?
So , do i have to always create an object with a temporary reference, than assign to real reference and release the temp reference like first code?
or is there any other way?
Yes and no. Generally, this is a common pattern:
// create the object, retain count 1
MyObject *myObject = [[MyObject alloc] init];
// increment the retain count in the setter
self.myObjectProperty = myObject;
// let go of the object before the end of the current method
[myObject release];
You can avoid the release, sort of, by using autorelease pools. More accurately, you indicate that you want the object to be released soon:
MyObject *myObject = [[[MyObject alloc] init] autorelease];
self.myObjectProperty = myObject;
// all done!
With many of the Apple-provided classes, you can use class methods other than alloc/init to get objects that are already autoreleased. Your example could be rewritten as:
MyObject *myObject = [[MyObject alloc] init];
myObject.myString = [NSMutableString stringWithFormat:#"bla"];
A final note: -retainCount is a blunt object. Particularly with NSStrings and other built-in classes, it may return results that are quite different from what you expect. Generally you should avoid it.

Understanding Cocoa Memory

What is the advantage of doing this:
NSArray *array = [[NSArray alloc] initWithObjects:#"Year", "#Capital", ..., nil];
self.hintArray = array;
[array release];
Instead of assigning directly to my class variable like this:
self.hintArray = [[NSArray alloc] initWithObjects:#"Year", "#Capital", ..., nil];
Why do we create a temporary local object then release it instead of just assigning to our class variable?
Others have already pointed out the memory issues, but here is the best way to do it in a single step:
self.hintArray = [NSArray arrayWithObjects:#"Year", "#Capital", ..., nil];
The convenience class method +arrayWithObjects returns an array that has already been autoreleased, so you simply don't need to worry about it any more. Your property accessor will take care of copying or retaining it. (assuming, of course, that your hintArray property is set up as a retain or copy property).
You could, but you have to remember to release it once before moving on. The assignment to self.hintArray (assuming it is a synthesized setter that retains on set) will bump the retainCount:
NSArray *array = [[NSArray alloc] initWithObjects:...]; // retainCount is 1
self.hintArray = array; // retainCount is 2
[array release]; // retainCount is 1
and:
self.hintArray = [[NSArray alloc] initWithObjects:...]; // retainCount is 2:
// one for the alloc
// and one for the assign
[self.hintArray release]; // retainCount is 1
Because in the Objective-C reference counted memory management scheme the creation of the array will increment the reference count and if you do not store the return value in a variable you can send a release message to you will have no way to decrement that count and will introduce a memory leak.