Objective C memory management question with NSArray - objective-c

I am loading an array with floats like this:
NSArray *arr= [NSArray arrayWithObjects:
[NSNumber numberWithFloat:1.9],
[NSNumber numberWithFloat:1.7],
[NSNumber numberWithFloat:1.6],
[NSNumber numberWithFloat:1.9],nil];
Now I know this is the correct way of doing it, however I am confused by the retail counts.
Each Object is created by the [NSNumber numberWithFloat:] method. This gives the object a retain count of 1 dosnt it? - otherwise the object would be reclaimed
The arrayWithObjects: method sends a retain message to each object.
This means each object has a retain cont of 2. When the array is de-allocated each object is released leaving them with a retain count of 1.
What have I missed?

The NSNumber numberWithFloat: method isn't returning a retained object.
In general unless you're using alloc, copy or new you can presume that you're getting a object that has a retain count of zero. As such the only retain that's taking place is when the NSArray has the objects added to it.
There's a good blog about such things over at: http://interfacelab.com/objective-c-memory-management-for-lazy-people/

There is no need to release those objects.
The arrayWithObjects: and numberWithFloat: creates object you do not own.

Related

NSMutableArray in loop leaks memory even when I explicitly release it

This loop leaks memory:
int64_t i,verylongnumber;
//misc. code
for(i=0;i<verylongnumber;i++){
NSMutableArray *myArray = [[NSMutableArray alloc] initWithObjects:
[NSNumber numberWithLongLong:65535],
[NSNumber numberWithLongLong:65535],
[NSNumber numberWithLongLong:65535],
[NSNumber numberWithLongLong:65535],
nil];
[myArray removeAllObjects];
[myArray release];
}
I've tried everything to keep it from leaking memory, but I can't. I think it has something to do with the NSNumbers. I assume they are created autoreleased, but does that mean I have to free them individually (i.e. use alloc)? How would I even do that? Create a separate variable for each NSNumber and insert that into the array? That seems like a lot of work. I tried [myArray removeAllObjects], but that made no difference. it is within my own thread with its own autorelease pool. I'm not sure if that makes a difference.
This fixed it:
I added an additional autorelease pool inside the loop:
int64_t i,verylongnumber;
//misc. code
for(i=0;i<verylongnumber;i++){
NSAutoreleasePool *pool2 = [[NSAutoreleasePool alloc] init];
NSMutableArray *myArray = [[NSMutableArray alloc] initWithObjects:
[NSNumber numberWithLongLong:65535],
[NSNumber numberWithLongLong:65535],
[NSNumber numberWithLongLong:65535],
[NSNumber numberWithLongLong:65535],
nil];
[myArray release];
[pool2 drain];
}
I'll take a stab at this..
You can remove [myArray removeAllObjects] as it is redundant. NSArray's do retain their objects, but they also release them when the array itself is deallocated.
The NSNumbers themselves are autoreleased. However if you do a very very large loop then that autoreleased memory won't actually be freed until the for loop exits and eventually the run loop as well (unless you have setup a separate NSAutoreleasePool somewhere).
So I can see how the memory usage would increase as this loop iterates, but at completion it should free the memory. How did you arrive at the conclusion that you have a leak?
Are you waiting to see if the objects get released in the near future?
Autoreleased objects are released at some point in the near future. In the case above, you're looping a very long number of times creating many objects. They will not get released within the scope of that code.
In a GUI app it means after the function returns when the run loop is being run. In a console app, it's when the pool is released.
Check out:
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmAutoreleasePools.html
Autorelease pools provide a mechanism whereby you can send an object a
“deferred” release message.
The key is the deferred point.
EDIT: (after comment)
Note that you can drain the pool. The other option is to create non-autoreleased numbers (alloc/init) and explicitly release in your long running loop. depending on the code, that may be desirable since draining the pool could release objects deferred for release which code later in that loop/scope assumes is still deferred. If you want to control it, then control it.
NSArray retains objects when they're added to NSArray. This means that NSArray takes ownership of that object. when the object is removed from NSArray or NSArray destroyed, the object is released (reference count -1). If object dont have any other owner then object is destroyed.
The following code will create memory leak
NSNumber *number = [[NSNumber alloc]initWithFloat:floatValue]; //reference count is 1, you are the owner
[aArray addObject:number] //reference count is 2, aArray is also owner.
So to remove memory leak , you shoul release number.
NSNumber *number = [[NSNumber alloc]initWithFloat:floatValue]; //reference count is 1, you are the owner
[aArray addObject:number] //reference count is 2, aArray is also owner.
[number release]; // reference count is 1, you are not owner og number
If you are adding autorelease object to NSArray, no need to release that object. when the autorelease pool is popped that object will loose its ownership.
In your example with each for loop, NSNumber is created while the old one is still hanging around in memory waiting for the autorelease pool to be released.

NSDictionary and EXC_BAD_ACCESS

Tried to find the answer here and eventually found a clue on another site. Posting here in case anyone searches here and has the same problem.
NSDictionary *d = [NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:#"foo", YES, 42, nil]
forKeys:[NSArray arrayWithObjects:#"bar", #"baz", #"count", nil]];
This produces:
Program received signal: "EXC_BAD_ACCESS"
What is the cause of this?
YES and 42 are not object pointers. You're trying to create an NSArray, which can only contain objects, and you're passing in values that are not pointers to objects. You'll crash for the same reason that
[YES description];
will crash -- YES is not a valid object pointer.
For one thing, in your array, YES and 42 are not objects. Try using [NSNumber numberWithInt:42] there. You should have got a compiler warning there.
A int nor a BOOL are objects and therefore cannot be part of a NSDictionary. Instead of using int's and bools use their object equivalent of a NSNumber. You can easily store a int using:
[NSNumber numberWithInt:(int)]
You can store a BOOL with:
[NSNumber numberWithBool:(BOOL)]

Warning "Potential leak of an object allocated on line ..." when declaring a 2D array

Initially i declared a 2D array in this way:
subUrb = [[NSArray alloc] initWithObjects:
[[NSArray alloc] initWithObjects:ALL_SUBURBS_LABEL, #"East",#"South", #"West", #"North", nil] ,
[[NSArray alloc] initWithObjects:ALL_SUBURBS_LABEL, #"Kuala Lumpur SubUrb1", #"Kuala Lumpur SubUrb2", nil],
[[NSArray alloc] initWithObjects:ALL_SUBURBS_LABEL, #"Jakarta SubUrb1",nil],
nil];
But when i try to 'analyze' the warnings of the project, i got three same type of issues in this chunk of code - "potential leak of an object allocated on line xxx"
I noticed that to get rid of it, i have to write sth like this:
subUrb =
[[NSArray alloc] initWithObjects:
[[[NSArray alloc] initWithObjects:ALL_SUBURBS_LABEL, #"East",#"South", #"West", #"North", nil] autorelease],
[[[NSArray alloc] initWithObjects:ALL_SUBURBS_LABEL, #"Kuala Lumpur SubUrb1", #"Kuala Lumpur SubUrb2", nil] autorelease],
[[[NSArray alloc] initWithObjects:ALL_SUBURBS_LABEL, #"Jakarta SubUrb1",nil] autorelease],
nil];
Then i will not get any analyze warnings. But i dun like this...it is not logical. This 2D array should never be released in my controller actually, the entire 2D array should be retained for the entire life time of the controller for a PickerView.
How should I declare the 2D array more elegantly?
1: NARC
If you send +alloc, you own that object and it is your responsibility to release it. Hence
[[NSArray alloc] initWithObjects:ALL_SUBURBS_LABEL, #"East",#"South", #"West", #"North", nil]
creates an array that’s owned by you, hence you should release it.
2: Cocoa collections own their elements
When you add an object to a Cocoa collection such as NSArray, the collection owns that object. It will also release all elements in the collection when they’re not used any longer.
3: Conclusion
Your code without -autorelease gives you ownership of both the outermost array and each inner array that’s an element of the outermost array. This means that you’re responsible for releasing those four arrays (one outermost, three innermost). However, since the outermost array already owns the innermost arrays, you probably don’t need to own them as well, hence -autorelease.
You get the ownership if you allocate an array with alloc init and so you have to release it as you do in your second code.
You can also use:
[[NSArray alloc] initWithObjects:
[NSArray arayWithObjects:first,second, nil], nil];
Your second method is correct. The point is that the enclosing array will take ownership of the nested objects, while the additional ownership lien you gain via alloc is left dangling. A marginally neater approach is to use the class method arrayWithObjects instead of alloc/initWithObjects/autorelease but it amounts to the same thing.
Even if the array is needed through the entire life of the controller, you should release it on the controller's dealloc method, otherwise the objects will leak.
If it is never released then both ways won't leak, but the second is more "correct". The NSArrays won't be autoreleased till the parent array is (never), so it is fine.
When you add a child array to the parent array, the parent array retains the child array. Thus increasing the retain count to 2. Since you're aren't keeping a reference to the child array(s) anywhere else the retain count should only be 1, hence you should use autorelease. So your second version is correct.

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.

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.