开发者

iPhone: Fast enumeration, not fast enough?

I am trying to loop through an NSSet that has about 6500 items in i开发者_StackOverflow中文版t. 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?


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.


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]];
}


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]];             
    }
    


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.


I just came across an interesting (and probably little-known) fact about iterating an NSSet:

The per-iteration overhead of the fast-enumeration loop varies by the collection type — basically, whether the collection forces the code to go back for more elements after each iteration. For an array, it does not; for a set, IIRC it does. The possibility makes it hard for the compiler to reason about memory across iterations of the loop (which would otherwise be a significant advantage of fast-enumeration), and in either case there's extra overhead because of the mandatory modification checks.

...

As a rule of thumb, iterating with a block is almost always best if you're not iterating over an array.

Source

Indeed I have found that iterating using the block-based methods is slightly faster than fast enumeration when you're using an NSSet. It's also slightly faster than using fast enumeration on the set's allObjects array.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜