Memory issue with NSArray and ARC - objective-c

I try to write a program in Objective C that calculates some stuff. Unfortunately there is a lot to calculate and the Program is running a loop with 10000 iterations. The total running time is around 5 minutes.
This isn't an issue but while it is running the memory the program uses goes up dramatically (above to 2GB) and I figure it is because of some Values that are not released. I narrowed the problem down to a creation of a NSArray inside a loop. The problematic code (changed it to concentrate on the problem) is this:
for (NSInteger k = 0; k<100000; k++) {
NSMutableArray *mutableTestArray = [NSMutableArray new];
for (NSInteger i=0; i<200; i++){
NSMutableArray *subArray = [NSMutableArray new];
for (NSInteger j=0; j<200; j++) {
//simplified version, normally the valus for sub array are dependent on some other factors
[subArray addObject:[NSArray arrayWithObjects:#"test", #"test", nil]];
}
[mutableTestArray addObject:subArray];
}
//do stuff with mutableTestArray
//change parameters that go into sub array
}
If I replace:
[subArray addObject:[NSArray arrayWithObjects:#"test", #"test", nil]];
with:
[subArray addObject:#"test"];
it works fine. So I guess I somehow have to release the Array I created. But how?
I have ARC enabled and as far as I know if it is enabled I can't release objects manually. It would be great if someone could help me with how to change the code or hint me somewhere where I can learn how to do memory management correctly.
Thanks

Just throw in some autoreleasepools :
#autoreleasepool {
NSMutableArray *subArray = [NSMutableArray new];
for (NSInteger j=0; j<200; j++) {
#autoreleasepool {
//simplified version, normally the valus for sub array are dependent on some other factors
[subArray addObject:[NSArray arrayWithObjects:#"test", #"test", nil]];
}
}
[mutableTestArray addObject:subArray];
}

[NSArray arrayWithObjects:#"test", #"test", nil]; returns with an autoreleased object and it will be released somewhere in the future, but not immediatelly.
Use [[NSArray alloc] initWithObjects:#"test", #"test", nil]; and the array will be released almost immediatelly, because of ARC.
If you wouldn't use ARC the code could be the following:
NSArray* tempArr = [[NSArray alloc] initWithObjects:#"test", #"test", nil];
// use the temporary variable
[subArray addObject:tempArr];
[tempArr release];
Because you are using ARC, you should remove the release.

Related

Trying to build polygon from NSString

So, I'm trying to build an array of CGPoints by breaking an NSString, typically look like this:
31.241854,34.788867;31.241716,34.788744;31.242547,34.787585;31.242661,34.787719
Using this code:
- (NSMutableArray *)buildPolygon:(NSString *)polygon
{
NSMutableArray *stringArray = [[NSMutableArray alloc] init];
[stringArray addObject:[polygon componentsSeparatedByString:#";"]];
NSMutableArray *polygonArray = [[NSMutableArray alloc] init];
for (int i=0; i < polygonArray.count; i++)
{
NSArray *polygonStringArray = [[NSArray alloc] init];
polygonStringArray = [[stringArray objectAtIndex:i] componentsSeparatedByString:#","];
CGFloat xCord = [[polygonStringArray objectAtIndex:0] floatValue];
CGFloat yCord = [[polygonStringArray objectAtIndex:1] floatValue];
CGPoint point = CGPointMake(xCord, yCord);
[polygonArray addObject:[NSValue valueWithCGPoint:point]];
}
NSLog(#"return polygonArray: %#", polygonArray);
return polygonArray;
}
But eventually I get an empty array.
What I'm doing wrong?
You're defining polygonArray as an empty array just before the start of your for loop. You should define polygonArray like:
NSArray *polygonArray = [polygon componentsSeparatedByString:#";"];
And you don't even need to bother with that stringArray variable.
You have confusion over alloc & init, and one simple typo...
The confusions first:
NSMutableArray *stringArray = [[NSMutableArray alloc] init];
This creates a new NSMutableArray and stores a reference to it in stringArray. All good so far.
[stringArray addObject:[polygon componentsSeparatedByString:#";"]];
And this obtains a reference to an NSArray ([polygon componentsSeparatedByString:#";"]) and adds it as a single element to the mutable array referenced by stringArray. There is nothing wrong per se with this, but it is not what you want in this case - you just want the array returned by componentsSeparatedByString:. You do this with:
NSArray *stringArray = [polygon componentsSeparatedByString:#";"];
Which takes the reference returned by componentsSeparatedByString: and stores it in the variable stringArray - no alloc or init required as you are not creating the array yourself. You don't even own this array, so if you are using MRC there is no need to release it later.
NSArray *polygonStringArray = [[NSArray alloc] init];
Now this allocates an immutable empty array and stores a reference to it in polygonStringArray. This is not a very useful array, as it contains nothing and cannot be modified! But you don't keep it around long...
polygonStringArray = [[stringArray objectAtIndex:i] componentsSeparatedByString:#","];
This obtains a reference to an array from componentsSeparatedByString: and stores it in polygonStringArray. If you are using MRC this will cause a leak - your pointless zero-length array created above will leak, and a new zero-length array will be created and leaked every time around the loop.
You are confused over allocation - you only need to allocate things you are creating; when you receive a reference to an already allocated object you only need to store that reference. (If using MRC you may also need to retain/release/autorelease it as well - but let's stick with ARC.) So all you needed here was:
NSArray *polygonStringArray = [[stringArray objectAtIndex:i] componentsSeparatedByString:#","];
Now your code is almost correct, just one typo:
for (int i=0; i < polygonArray.count; i++)
Well you are filling polygonArray in this loop and it starts off as empty, what you need is stringArray.count.
HTH

How to deallocate objects in NSMutableArray?

i have this Mutable Array:
NSMutableArray *points = [pgroute getPoints:self];
where [getPoint...] do this:
{
NSMutableArray *normPoints = [[NSMutableArray alloc] init];
[normPoints addObject:#""];
[...]
return normPoints;
}
now,
points is an array of objects, right?
is correct to release *points array in this way?
for (int i = 0; i < [points count]; i++) {
[(NSString *)[points objectAtIndex:i] release];
}
[points release];
or it is another correct way?
Xcode compiler, with RUN_CLANG_STATIC_ANALYZER tell me there is an
Incorrect decrement of the reference
count of an object that is not owned
at this point by the caller
How can i resolve this?
thanks,
alberto.
If you want to empty the array, just do this:
[points removeAllObjects];
If you want to release the array, you can even skip that and release it right away:
[points release];
The array will handle releasing the objects on its own. Then again if you're only adding NSString literals (#"using this notation") to the array, they don't need to be released since they are constants. That's a different story of course; my point is that NSMutableArray will deal with releasing stuff where necessary for you.

Size of the NSMutable Array in objective C?

I want to ask the size of the NSMutable Array can be 2000? If not, is it possible to open an array to store 2000 elements. The elements is the user-defined object. Thank you.
The answer is that an NSMutableArray always starts with a size of zero. If you want it to be so you can do something like this:
NSMutableArray* anArray = [NSMutableArray arrayWithSize: 2000];
[anArray replaceObjectAtIndex: 1999 withObject: foo];
you need to prefill the array with NSNull objects e.g.
#implementation NSArray(MyArrayCategory)
+(NSMutableArray*) arrayWithSize: (NSUInteger) size
{
NSMutableArray* ret = [[NSMutableArray alloc] initWithCapacity: size];
for (size_t i = 0 ; i < size ; i++)
{
[ret addObject: [NSNull null]];
}
return [ret autorelease];
}
#end
Edit: some further clarification:
-initWithCapacity: provides a hint to the run time about how big you think the array might be. The run time is under no obligation to actually allocate that amount of memory straight away.
NSMutableArray* foo = [[NSMutableArray alloc] initWithCapacity: 1000000];
NSLog(#"foo count = %ld", (long) [foo count]);
will log a count of 0.
-initWithCapacity: does not limit the size of an array:
NSMutableArray* foo = [[NSMutableArray alloc] initWithCapacity: 1];
[foo addObject: #"one"];
[foo addObject: #"two"];
doesn't cause an error.

How to append values to an array in Objective-C

I'm doing this:
for(int i=0;i>count;i++)
{
NSArray *temp=[[NSArray alloc]initWIthObjects]i,nil];
NSLog(#"%i",temp);
}
It returns to me 0,1,2,3....counting one by one, but I want an array with appending these values {0,1,2,3,4,5...}.
This is not a big deal, but I'm unable to find it. I am new to iPhone.
NSMutableArray *myArray = [NSMutableArray array];
for(int i = 0; i < 10; i++) {
[myArray addObject:#(i)];
}
NSLog(#"myArray:\n%#", myArray);
This code is not doing what you want it to do for several reasons:
NSArray is not a "mutable" class, meaning it's not designed to be modified after it's created. The mutable version is NSMutableArray, which will allow you to append values.
You can't add primitives like int to an NSArray or NSMutableArray class; they only hold objects. The NSNumber class is designed for this situation.
You are leaking memory each time you are allocating an array. Always pair each call to alloc with a matching call to release or autorelease.
The code you want is something like this:
NSMutableArray* array = [[NSMutableArray alloc] init];
for (int i = 0; i < count; i++)
{
NSNumber* number = [NSNumber numberWithInt:i]; // <-- autoreleased, so you don't need to release it yourself
[array addObject:number];
NSLog(#"%i", i);
}
...
[array release]; // Don't forget to release the object after you're done with it
My advice to you is to read the Cocoa Fundamentals Guide to understand some of the basics.
A shorter way you could do is:
NSMutableArray *myArray = [NSMutableArray array];
for(int i = 0; i < 10; i++) {
[myArray addObject:#(i)];
}
NSLog(#"myArray:\n%#", myArray);

Return mutable or copy

If you have a method in objective c that builds an array or dictionary using a mutable object, should you then copy the object, or return the mutable version? This is probably a case of opinion but I have never been able to make up my mind. Here are two examples to show what I am talking about:
- (NSArray *)myMeth
{
NSMutableArray *mutableArray = [NSMutableArray array];
for (int i=0; i<10; i++) {
[mutableArray addObject:[NSNumber numberWithInt:i]];
}
return mutableArray;//in order for calling code to modify this without warnings, it would have to cast it
}
- (NSArray *)myMeth
{
NSMutableArray *mutableArray = [[NSMutableArray alloc] init];
for (int i=0; i<10; i++) {
[mutableArray addObject:[NSNumber numberWithInt:i]];
}
NSArray *array = [[mutableArray copy] autorelease];
[mutableArray release];
return array;//there is no way to modify this
}
It depends what the method will be used for, or what the intented use for the returned array is.
By convention it is considered normal to copy and autorelease the mutable array before returning it, thereby complying to the object ownership conventions and protecting the data from being changed once it's returned.