Understanding Cocoa Memory - objective-c

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.

Related

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.

Returning an NSArray without Leaking?

I have been struggling with the best pattern for returning an array from a static method.
In my static method getList (in the BIUtility Class), I am allocating an NSArray to return. in the return line, I do:
return [array autorelease];
Then in the calling method, I am allocating an array like this:
NSArray * list = [[[NSArray alloc] initWithArray:[BIUtility getList]] retain];
Later I release the list using:
[list release];
I think this is causing a memory leak as the retain is increasing the retain count one too many. However, if I do not do the retain, I get a Bad_Exec because it has already freed the class.
I feel like I am overthinking this and there must be a typical pattern. I have been looking all over the place and I cannot find a "best practice".
I appreciate your help.
You should replace:
NSArray * list = [[[NSArray alloc] initWithArray:[BIUtility getList]] retain];
With:
NSArray * list = [[BIUtility getList] retain];
This is because getList actually returns a pointer to the NSArray.
If it were a mutable array, however, you should say [[BIUtility getList] copy]; so that you don't accidentally mutate an array that another object has a reference to.
If you are curious, you were getting a memory leak because your original statement increments two counters, while you only release one later.
These parts of the statement increase counts:
[anObject]] retain]
[anClassname alloc]
[anObject copy] will also create an object with a count of 1.

Memory leak for NSDictionary loaded by plist file

I have a memory leak problem that just can not understand! Watch this initialization method:
- (id)initWithNomeCompositore:(NSString *)nomeCompositore nomeOpera:(NSString *)nomeOpera {
if (self = [super init]) {
NSString *pathOpere = [[NSBundle mainBundle] pathForResource:kNomeFilePlistOpere ofType:kTipoFilePlist];
NSDictionary *dicOpera = [NSDictionary dictionaryWithDictionary:
[[[NSDictionary dictionaryWithContentsOfFile:pathOpere]
objectForKey:nomeCompositore]
objectForKey:nomeOpera]];
self.nomeCompleto = [[NSString alloc] initWithString:nomeOpera];
self.compositore = [[NSString alloc] initWithString:nomeCompositore];
self.tipologia = [[NSString alloc] initWithString:[dicOpera objectForKey:kKeyTipologia]];
}
return self;}
Then this little variation (note self.tipologia):
- (id)initWithNomeCompositore:(NSString *)nomeCompositore nomeOpera:(NSString *)nomeOpera {
if (self = [super init]) {
NSString *pathOpere = [[NSBundle mainBundle] pathForResource:kNomeFilePlistOpere ofType:kTipoFilePlist];
NSDictionary *dicOpera = [NSDictionary dictionaryWithDictionary:
[[[NSDictionary dictionaryWithContentsOfFile:pathOpere]
objectForKey:nomeCompositore]
objectForKey:nomeOpera]];
self.nomeCompleto = [[NSString alloc] initWithString:nomeOpera];
self.compositore = [[NSString alloc] initWithString:nomeCompositore];
self.tipologia = [[NSString alloc] initWithString:#"Test"];
}
return self;}
In the first variant is generated a memory leak, the second is not! And I just can not understand why! The memory leak is evidenced by Instruments, highlighted the line:
[NSDictionary dictionaryWithContentsOfFile:pathOpere]
This is the dealloc method:
- (void)dealloc {
[tipologia release];
[compositore release];
[nomeCompleto release];
[super dealloc];}
Remember that alloc returns an object that you own.
If you declared your three string properties as retain, assigning those objects to your properties means you now own each one twice—once because you allocked it, and again because you assigned it to your property. The objects remain alive because nothing releases their second ownerships.
If you declared the properties as copy (which is the correct way to declare an NSString property), assigning the object there stores a copy as the value of the property. You do nothing further with the original objects, which remain alive because nothing releases them.
Either way, that is your leak.
The property should be declared as copy; if it already is, don't try to fix the leak by changing that.
You should not use property access here. Remember that assigning to a property is a set<PropertyName>: message, and that your object is not fully initialized yet. Sending a message to an incompletely-initialized or incompletely-deallocated object is asking for trouble, particularly when subclasses are involved, since they may override the accessor methods in ways the superclass doesn't expect.
So, in init only, assign directly to the instance variables. In dealloc only, send release messages directly to the objects in the instance variables. Everywhere else, use property accesses.
You also should not use alloc and initWithString: here. It'll work, but the convention is to send copy messages to the objects you already have, the same as the properties would do. Send copy messages to your input string objects, then assign the copies to your instance variables.
When you do use property accesses, use the convenience constructors (stringWith…:, for example), as these return objects that you do not own. When you assign these objects to your copy-declared properties, you will actually be storing copies that you do own.
The other way would be to use alloc and initWithWhatever:, then immediately autorelease that object before assigning it to the property; this way creates an object that you own, then immediately gives up ownership before assigning it to the property.
Try
nomeCompleto = [[NSString alloc] initWithString:nomeOpera];
compositore = [[NSString alloc] initWithString:nomeCompositore];
tipologia = [[NSString alloc] initWithString:[dicOpera objectForKey:kKeyTipologia]];
or
self.nomeCompleto = nomeOpera;
self.compositore = nomeCompositore;
self.tipologia = [dicOpera objectForKey:kKeyTipologia];
instead of self.xxx = [[yyy alloc] init...].
In the original code, the RHS of the assignment returns an object of retain count +1, and if you make the #property having (retain) or (copy), the final retain count would be +2. Therefore, even if you release these in -dealloc, the net retain count is +1, causing a memory leak.
BTW, there's no point calling +dictionaryWithDictionary:. Just use
NSDictionary* dicOpera = [[[NSDictionary dictionaryWithContentsOfFile:pathOpere]
objectForKey:nomeCompositore]
objectForKey:nomeOpera];

Memory Management for shared array elements

I am a little confused about releasing memory for array elements that are shared by multiple arrays. Here's the scenario:
Manager class has an instance variable NSMutableArray* mgrArray
Helper class has an instance variable NSMutableArray* helperArray.
Manager's init method:
NSMutableArray* mgrArray = [[[NSMutableArray alloc] init] autorelease];
for (int i=0; i<10; i++) {
Food *f = [[[Food alloc] initWithType:#"Fruit"] autorelease];
[mgrArray addObject:f];
}
Helper's init method:
NSMutableArray* helperArray = [[[NSMutableArray alloc] init] autorelease];
The manager object passes some of the mgrArray elements to Helper class to store for Helper's own access purposes (say for efficiency). Some Manager method that does this:
Food *e1 = [mgrArray objectAtIndex:3];
Food *e2 = [mgrArray objectAtIndex:5];
Food *e3 = [mgrArray objectAtIndex:7];
[helper. helperArray addObject:e1];
[helper. helperArray addObject:e2];
[helper. helperArray addObject:e3];
Question 1: when adding e1 to helperArray, should it be copied or retained or is it alright as written above?
Question 2: who should release the memory of the food objects and how?
If you put an object in an array, it retains it. If you remove an object from an array, the array releases it.
It is that simple; ownership -- retains -- should always be bounded by lines of encapsulation.
If you need to ensure that an object remains alive when you are using it, retain it. The one edge case is if you do something like:
foo = [someArray objectAtIndex: 0];
[someArray removeObjectAtIndex: 0];
At that point, foo may have been released. To fix:
foo = [someArray objectAtIndex: 0];
[foo retain];
[someArray removeObjectAtIndex: 0];
.. do stuff ..
[foo release];
Question 1: when adding e1 to
helperArray, should it be copied or
retained or is it alright as written
above?
Simply add e1 to the helperArray. Done.
Question 2: who should release
the memory of the food objects and
how?
When the array is released to the point of being dealloc'd, all objects contained within the array will be released (but not necessarily deallocated unless nothing else holds a retain).
An object will be retained when added to an array and released when removed from an array.
If you are using an autoreleased object there is nothing else to do.
If you are using a regular object, you can release it after it is added to the first array.

Assigning values to Instance variables in Objective C

The function I'm looking at:
-(void)viewDidLoad {
NSBundle *bundle = [NSBundle mainBundle];
NSString *plistPath = [bundle pathForResource:#"statedictionary" ofType:#"plist"];
NSDictionary *dictionary = [[NSDictionary alloc] initWithContentsOfFile:plistPath];
self.statesZips = dictionary;
[dictionary release];
NSArray *components = [self.stateZips allKeys];
NSArray *sorted = [components sortedArrayUsingSelector:#selector(compare:)];
self.States = sorted;
NSString *selectedState = [self.states objectAtIndex:0];
NSArray *array = [stateZips objectForKey: selectedState];
self.zips = array;
}
Why is an NSDictionary allocated, then assigned to a pointer called *dictionary, and then assigned to the instance variable stateZips? Why not allocate it and assign it directly to the instance variable and save memory of creating and releasing another NSDictionary? The same methodology is always followed, including later in this function with the NSArray...
NSDictionary *dictionary = [[NSDictionary alloc] initWithContentsOfFile:plistPath];
self.statesZips = dictionary;
[dictionary release];
Also, this sorting puts the keys from a hash table (dictionary) in alphabetical order. I'm not sure I understand this line:
NSArray *sorted = [components sortedArrayUsingSelector:#selector(compare:)];
No one seems to have addressed the fact that the line
self.statesZips = dictionary;
is not directly an instance variable assignment. stateZips is a property, and so that line of code calls the setStateZips: method. That method retains or copies the dictionary, so unless the viewDidLoad method intends to use it again for some purpose, it's not needed any longer. That makes it OK to release it.
The previous line:
[[NSDictionary alloc] initWithContentsOfFile:plistPath];
allocates an object. That makes it your responsibility to release it once you don't need it any more. After assigning it to the statesZips property, it's no longer needed, so it's released and you shouldn't use dictionary any more. You'll notice that later code only refers to self.stateZips, not dictionary.
In the case of the NSArray later in the method, viewDidLoad does not allocate the object, so that method is not responsible for calling release on it. The rule of thumb is that if you alloc it, you're responsible for making sure it gets released. Otherwise, it's not your problem.
Sorting the array uses the sortedArrayUsingSelector: method. A selector identifies a method in Objective-C. And the #selector is the literal syntax for selectors (kind of like how #"" is the literal syntax for NSString objects). So, what that code says, is "give me an array where the objects in components are sorted, and use the compare: method to compare each object when you do the sort. When it sorts the array, it will call compare: on the objects in the array to determine how to put them in order.
The statesZips property is probably retained, that's the reasoning.
When the NSDictionary is first allocated, its retain count is 1. When it's assigned to statesZips, the retain count becomes 2. When it's released, the retain count drops to 1, which is usually the desired outcome.
Note that the code below would have produced (almost) the same result:
self.statesZips = [NSDictionary dictionaryWithContentsOfFile:plistPath];
because dictionaryWithContentsOfFile returns an autoreleased object.
As a convention, class methods like [NSDictionary dictionary] return autoreleased objects (which automatically get released after some time), while the usual alloc-init method (as in [[NSDictionary alloc] init]) return retained objects.
I suggest you read the Memory Management Programming Guide for Cocoa for further information.
EDIT: I must have missed the last part of your question when I first read it, but Barry has already answered that part.
This code uses reference-counted memory management (not the automatic garbage collection memory management available in Objective-C 2.0 on OS X). When any object (in this case, the NSDictionary and the NSArray) are alloc'd, the caller is responsible for calling -release on that instance. Failing to call release causes a memory leak. The code could have been written as
self.statesZips = [[[NSDictionary alloc] initWithContentsOfFile:plistPath] autorelease];
but at the expense of less explicit memory management (relying on NSAutoreleasePool to release the alloc'd instance at the end of the event loop iteration.
the call
[components sortedArrayUsingSelector:#selector(compare:)];
returns an array of whose elements come from components but according to the return value of calling [elem1 compare:elem2] to compare two array elements.