should variable be released or not? iphone-sdk - iphone

In the following piece of code (from a book) data is an NSDictionary *data; defined in the header (no property).
In the viewDidLoad of the controller the following occurs:
- (void)viewDidLoad {
[super viewDidLoad];
NSArray *keys = [NSArray arrayWithObjects:#"home", #"work", nil];
NSArray *homeDVDs = [NSArray arrayWithObjects:#"Thomas the Builder", nil];
NSArray *workDVDs = [NSArray arrayWithObjects:#"Intro to Blender", nil];
NSArray *values = [NSArray arrayWithObjects:homeDVDs, workDVDs, nil];
data = [[NSDictionary alloc] initWithObjects:values forKeys:keys];
}
Since I am really new to objective-c can someone explain to me why I do not have to retain the variables keys,homeDVDs,workDVDs and values prior exiting the function? I would expect prior the data allocation something like:
[keys retain];
[homeDVDs retain];
[workDVDs retain];
[values retain];
or not? Does InitWithObjects copies (recursively) all objects into a new table?
Assuming we did not have the last line (data allocation) should we release all the NSArrays prior exiting the function (or we could safely assumed that all NSArrays will be autoreleased since there is no alloc for each one?)
Thanks!!!!

In Core Foundation, most of static calls to methods such as arrayWithObjects: return autoreleased instances, which means you don't need to (and even must not!) release it by yourself.
So the situation after your code is executed is that all arrays you created with arrayWithObjects: calls are autoreleased, but are retained when added to other array or dictionary. So homeDVDs and workDVDs are retained when added to the values array, and keys and values arrays are both retained when added to the data dictionary.
So you don't need to explicitly release your arrays, but you'll need to release your data dictionary at some point (perhaps in dealloc method implementation).

NSDictionary copies the key field and retains the values. All your other instances are autoreleased, so you're not missing any releases or retains (assuming you release data in the dealloc method).

Related

About memory management in iPhone

Please clarify the following thing.
Everyone knows that; if we are using alloc, retain, new and etc..., we have to release it. For remaining things, we have to use autorelease. My doubt is;
-(NSArray*)getArray{
NSArray *array = [[NSArray alloc] initWithObjects:#"1", #"2", #"3", #"4", #"5", nil];
return [array autorelease];
}
NSArray *arr = [self getArray];
---
---
What we have to do the arr?
EDIT:
NSString *str = [NSString stringWithFormat:#"Welcome..."];
If we are using the above statement, we should call autorelease. But I want to know, what is happening in the stringWithFormat:method. How it is returning NSString.
Thanks.
If you are planning to return the array, go ahead and use the [NSArray arrayWithObjects:#"1", #"2", etc, nil] instead.
You then just need to remember to retain it if you want to hold on to it for longer then the autorelease pool will hold it.
The autorelease pool will give it a retain count of 1, and then automatically decrement it by 1 when the release pool gets called. Without retaining it in the calling function, this object will eventually disappear.
You don't have to do anything with arr since you didn't explicitly alloc, copy, new, or retain it in its current scope. It's already been added to the autorelease pool so it'll automatically be cleaned up once you're done with it.
EDIT: In your edited question, [NSString stringWithFormat:] returns an autoreleased string. It's basically doing the same thing as you're doing in your getArray method. It builds a NSString (or related) object and autoreleases it before it's returned.
You should retain:
[[self getArray] retain];
Or return non-autoreleased object in getArray.
Your getArray method is returning an NSArray that _will_be_ released when the stack fully unwinds.
In the method where you are calling your getArray method, it is safe to use the NSArray, but if you want to keep it, and use it after your current method returns, you will need to retain the NSArray with [arr retain].
Answer to your new question
Class methods, like [NSString stringWithFormat:] or like [NSURL URLWithString:] return objects that have been autoreleased. This is a convention, a standard practice in UIKit and the Apple frameworks.

Objective C two-dimensional array memory issues

I'm trying to declare a two-dimensional array as an instance variable in Objective C. I've got the NSMutableArray in the header (data), along with the #property (nonatomic, retain). In viewDidLoad: I have:
data = [[NSMutableArray alloc] init];
[data addObject:[[NSArray alloc] initWithObjects:#"Cheese", #"Meat", #"Veggie", nil]];
[data addObject:[[NSArray alloc] initWithObjects:#"Sandwich", #"Soup", #"Stew", nil]];
I can NSLog the array within the method and it is correct, however when I try to Log it from a separate method I get nothing (just "#"), and if I try to access with
NSInteger num = [[data objectAtIndex:component] count];
it crashes with no error in the log. I'm sure this is something to do with not allocating memory properly, however I am new to Obj C and haven't worked with a C-style language in many years. FWIW, I have tried many variants of this that all fail, including using NSArray instead of mutable, [NSArray arrayWithObjects] instead of [[NSArray alloc] initWithObjects], and every combination in between.
try creating the outer array like this:
self.data = [NSMutableArray arrayWithCapacity:2]; // assuming you're only adding 2 inner arrays.
The following may be a right way.
self.data = [NSMutableArray array];
[data addObject:[NSArray arrayWithObjects:#"Cheese", #"Meat", #"Veggie", nil];
[data addObject:[NSArray arrayWithObjects:#"Sandwich", #"Soup", #"Stew", nil];
Note that, as #jamihash commented above, you need self.data to properly retain the array. And, there is no need to alloc the NSArray which you are adding to data.
As a side issue, you're retaining the child arrays twice. They get retained when you add them to your NSMutableArray, so you should probably autorelease them on creation or create them with one of the NSArray methods that returns an autoreleased array.
Your code by itself shouldn't crash. You should look into where and when you release and retain the NSMutableArray. You could post more of the code and I'm sure somebody will spot the problem.

Will this Objective-C, nested NSArray cause a memory leak on iPhone?

NSArray *tLines = [[NSArray alloc] initWithObjects:
[[NSArray alloc] initWithObjects:#"Matt", #"David", nil],
[[NSArray alloc] initWithObjects:#"Bob", #"Ken", nil], nil];
self.lines = tLines;
[tLines release];
I'm allocing NSArrays within an NSArray, will the nested arrays get released when I call [lines release];?
Why don't you use a convenience method? No need to release memory.
NSArray *tLines = [NSArray arrayWithObjects:
[NSArray arrayWithObjects:#"Matt", #"David", nil],
[NSArray arrayWithObjects:#"Bob", #"Ken", nil], nil];
Yes, this will cause a leak.
When created, the sub-arrays have a retain count of 2 (from alloc and adding to the array). When tLines is released, this is decremented to 1; which isn't enough to cause it to be deallocated; meaning that they leak.
Use +[NSArray arrayWithObjects] or autorelease the arrays on creation.
Remember: NARC (New, Alloc, Retain, Copy) should always be balanced with a release or autorelease.
No, you should not do this. When you alloc the arrays, you need to release them. The containing array will handle memory management for its objects (retaining them when they're added, releasing when they're removed), but that is beside the responsibility of the object that created to the arrays to properly release them. You should either use a convenience constructor to create the arrays or else create them one at a time and release them after they've been added to the master array.
I'm pretty sure it will leak, unless you release each object yourself before clearing the Array...
Or try using autorelease option at the creation, it'll do the work for you...

Should I release array returned from [NSMutableDictionary ValueForKey: ]

I have a NSMutableDictionary with the key being the first alphabet of the name of an object. The view is something like the 'Contacts' tab on iphone. Additionally user can select individual objects in the list.
In the code I find each selected object to process them further.
NSMutableArray *objectsToAdd = [[NSMutableArray alloc] init];
NSMutableArray *array = nil;
for (NSString *key in self.nameIndex) {
array = (NSMutableArray *)[searchedNameDictionary valueForKey:key];
for (Objects *eachObject in array) {
if (eachObject.objectIsSelected){
[objectsToAdd addObject:eachObject];
}
}
}
[array release];
-(void)dealloc()
{
[searchedNameDictionary release];
}
The app is crashing where I release searchedNameDictionary, with the message that the deallocated object is being referenced.
Now if in the code above, I remove [array release] the app works fine.
My question is does releasing 'array' is actually releasing the objects in searchedNameDictionary, which is what seems to be happening.
Would not releasing array cause memory leak?
You shouldn't release returned object unless they come from an alloc or copy method.
Returned objects are autoreleased otherwise, if you want to keep it around your should retain it right after receiving it.
array = (NSMutableArray *)[searchedNameDictionary valueForKey:key];
This returns an autoreleased object, thus you don't need to release it.
There are some other...issues with your code too, but mostly style things. Get rid of the [array release] and you're good to go as far as that issue is concerned.

Cocoa Touch: What to release, when to release it?

I'm trying hard to understand when and what I must relase in Cocoa Touch as it doesn't have garbage collection.
This code block is from apples iphone sample PeriodicElements and they release anElement and rawElementArray but not thePath, firstLetter, existingArray and tempArray?
I would have thought that at least tempArray and existingArray should be released.
Could some brainy person please explain to me why?
Thanks :)
- (void)setupElementsArray {
NSDictionary *eachElement;
// create dictionaries that contain the arrays of element data indexed by
// name
self.elementsDictionary = [NSMutableDictionary dictionary];
// physical state
self.statesDictionary = [NSMutableDictionary dictionary];
// unique first characters (for the Name index table)
self.nameIndexesDictionary = [NSMutableDictionary dictionary];
// create empty array entries in the states Dictionary or each physical state
[statesDictionary setObject:[NSMutableArray array] forKey:#"Solid"];
[statesDictionary setObject:[NSMutableArray array] forKey:#"Liquid"];
[statesDictionary setObject:[NSMutableArray array] forKey:#"Gas"];
[statesDictionary setObject:[NSMutableArray array] forKey:#"Artificial"];
// read the element data from the plist
NSString *thePath = [[NSBundle mainBundle] pathForResource:#"Elements" ofType:#"plist"];
NSArray *rawElementsArray = [[NSArray alloc] initWithContentsOfFile:thePath];
// iterate over the values in the raw elements dictionary
for (eachElement in rawElementsArray)
{
// create an atomic element instance for each
AtomicElement *anElement = [[AtomicElement alloc] initWithDictionary:eachElement];
// store that item in the elements dictionary with the name as the key
[elementsDictionary setObject:anElement forKey:anElement.name];
// add that element to the appropriate array in the physical state dictionary
[[statesDictionary objectForKey:anElement.state] addObject:anElement];
// get the element's initial letter
NSString *firstLetter = [anElement.name substringToIndex:1];
NSMutableArray *existingArray;
// if an array already exists in the name index dictionary
// simply add the element to it, otherwise create an array
// and add it to the name index dictionary with the letter as the key
if (existingArray = [nameIndexesDictionary valueForKey:firstLetter])
{
[existingArray addObject:anElement];
} else {
NSMutableArray *tempArray = [NSMutableArray array];
[nameIndexesDictionary setObject:tempArray forKey:firstLetter];
[tempArray addObject:anElement];
}
// release the element, it is held by the various collections
[anElement release];
}
// release the raw element data
[rawElementsArray release];
// create the dictionary containing the possible element states
// and presort the states data
self.elementPhysicalStatesArray = [NSArray arrayWithObjects:#"Solid",#"Liquid",#"Gas",#"Artificial",nil];
[self presortElementsByPhysicalState];
// presort the dictionaries now
// this could be done the first time they are requested instead
[self presortElementInitialLetterIndexes];
self.elementsSortedByNumber = [self presortElementsByNumber];
self.elementsSortedBySymbol = [self presortElementsBySymbol];
}
They create rawElementsArray by sending +alloc to the class, therefore this object is owned by the code in the sample above and must be released. Similarly with anElement. Note that thePath and tempArray are not created by sending +alloc, +new or -copy messages, therefore the calling code is not responsible for the lifetime of those objects. Please have a look at this collection of Cocoa memory management articles:
http://iamleeg.blogspot.com/2008/12/cocoa-memory-management.html
The reason you don't have to release tempArray is because it's been allocated and then autoreleased right away. Autorelease is a method of scheduling a release call sometime in the future, so that the caller of an API doesn't have to do any explicit releasing of the result.
Matt Dillard has provided a detailed explanation of Objective C's memory management strategy and has explained it much better than I can.
The convention is that when you create an object using a class method it should have been autoreleased. This means that at the end of the run loop when the autorelease pool is flushed these objects will be released. However, if you create something using +alloc] -init] or -copy, -mutableCopy or +new (which is the same as +alloc] -init]) then it will not have been autoreleased.
For example:
NSArray *array1 = [NSArray arrayWithObject:#"foo"];
NSArray *array2 = [[NSArray alloc] initWithObject:#"foo"];
Array1 will be autoreleased and you don't need to worry about it. Array2 will need to be manually released. Or alternatively you could do:
NSArray *array2 = [[[NSArray alloc] initWithObject:#"foo"] autorelease];
Which is pretty much what +arrayWithObject: does.
Of course this leads to an important consideration with the lifetime of instance variables. If you create the instance variable as with array2 then it will be fine as it has a retain count of 1. However, array1 will need to be retained otherwise it will be autoreleased at the end of the runloop, giving it a retain count of 0 and so it will be freed and you will be left with a dangling pointer.