views:

116

answers:

4

I am trying to loop through an NSSet that has about 6500 items in it. I am using:

for (id Location in sortedArray) {
            loc = [sortedArray objectAtIndex:i];
            cord = [cord stringByAppendingString:[NSString stringWithFormat:@"%f,%f ",[loc.longitude doubleValue],[loc.latitude doubleValue]]];
            i++;
        }

This works fine, but it seems that it is NOT fast enough. It gets to about item 5700 and I get the following error:

Program received signal:  “0”.
Data Formatters temporarily unavailable, will re-try after a 'continue'. (Unknown error loading shared library "/Developer/usr/lib/libXcodeDebuggerSupport.dylib")

Is there a way to loop through data quicker than this? It takes about 20 seconds or more, and it seems like making the user wait this long it too much!

Ideas?

+6  A: 

Three things:

  • You're looping through an array, not a set. If you don't care about order, loop over the set.
  • You're not using the "fast enumeration" API.
  • +[NSString stringWithFormat:] returns an autoreleased object. -[NSString stringByAppendingString:] returns another autoreleased object. You're using up a lot of memory.
  • -[NSString stringByAppendingString:] makes a copy of itself and then appends the new string. Every time you make a string, the amount of copying increases; your algorithm is O(n2). 65002 is quite big.

Additionally, it looks like you're using your own location class. Change it to return doubles instead of (I assume) NSNumber*s. Definitely do not return NSStrings; converting from string to double is slooooooow. Alternatively, return a CLLocationCoordinate2D (a struct of two doubles) to avoid an additional method call.

Let me shamelessly rewrite your code:

NSMutableString * cord = [NSMutableString stringWithCapacity:cord.count*20];
for (Location * loc in sortedArray) {
  [cord appendFormat:@"%f,%f ",[loc.longitude doubleValue],[loc.latitude doubleValue]];
}
tc.
Thanks. I need to sort my set first, that is why it is an array. I fixed the loop to actually use fast enumeration as well as an NSMutableString and it really sped things up, allowing me to loop through all 6500 values.I am using my own Location class coming out of core data, which creates doubles as NSNumbers. Not sure how to change that. :(
Nic Hubbard
make your functions return a double instead of an NSNumber*? It's not particularly difficult.
tc.
+1  A: 

Not sure what cause your program error, but there's two things you can improve:

  1. Once you're enumerating your container using fast enumeration there's no need to get item by index
  2. Use NSMutableString to accumulate values

    NSMutableString *cord = [NSMutableString string];
    for (CLLocation* loc in sortedArray) {
      [cord appendFormat@"%f,%f ",[loc.longitude doubleValue],[loc.latitude doubleValue]];             
    }
    
Vladimir
+7  A: 

You set up a loop using Fast Enumeration, then you ignore it.

for (id Location in sortedArray) {
    loc = [sortedArray objectAtIndex:i];

The first line sets up a loop-local variable named Location which will, on each iteration, point to an item in the array. But you are ignoring that variable and using a second variable, loc, and fetching the value from the array a second time. You should rewrite it as:

for (id loc in sortedArray) {
    cord = [cord stringByAppendingString:...]
}

While we're at it, the way you are building the cord string is nuts. You are creating a new string on each iteration through the loop. It would be smarter to use NSMutableString and call appendFormat: on each iteration. Then you won't fill your autorelease pool with thousands of unused NSString objects. So something like this:

NSMutableString *cord = [NSMutableString string];
for (id loc in sortedArray) {
    [cord appendFormat:...];
}

Both of those changes will speed up your code and cut down memory usage significantly, and will probably eliminate whatever was causing the weird error you encountered.

benzado
Thank you, I am new to this and still learning. I made the changes like you said, and it significantly sped up my code, allowing me to loop through all 6500 items.
Nic Hubbard
A: 

It looks like you're app is terminated from hogging the main thread while this iteration takes place.

You should use an NSOperation to wrap up the task and perform it asynchronously. You will need to call back to the main thread for completion, but attempting to do this work on the main thread is a broken user experience – even if it only takes 3 seconds.

ohhorob