3

My code below is causing my app to quit i.e. get black screen and then see in debugger console: Program received signal: “0”.

Basically it is causing problem when my orderArray has count of 2000 or more. I am using iPhone 3GS with iOS 4.2

Question: Is there a more efficient and less memory consuming way to create my long outStr?

NSString *outStr = @"";

for (int i = 0; i < count; i++) {
    NSDictionary *dict = [[ARAppDelegate sharedAppDelegate].orderArray objectAtIndex:i];
    outStr = [outStr stringByAppendingFormat:@"%@,%@,%@,%@\n", 
              [dict valueForKey:@"CODE"], 
              [dict valueForKey:@"QTY"],
              [[ARAppDelegate sharedAppDelegate].descDict valueForKey:[dict valueForKey:@"CODE"]],
              [[ARAppDelegate sharedAppDelegate].priceDict valueForKey:[dict valueForKey:@"CODE"]]];

}

Update: Thanks to very kind people who helped, below is my modified code:

NSArray *orderA = [ARAppDelegate sharedAppDelegate].orderArray;
NSDictionary *descD = [ARAppDelegate sharedAppDelegate].descDict;
NSDictionary *priceD = [ARAppDelegate sharedAppDelegate].priceDict;
NSMutableString *outStr = [[[NSMutableString alloc] init] autorelease];
for (int i = 0; i < [orderA count]; i++) {
    NSDictionary *dict = [orderA objectAtIndex:i];
            NSString *code = [dict valueForKey:@"CODE"];
    [outStr appendFormat:@"%@,%@,%@,%@\n", 
              code, 
              [dict valueForKey:@"QTY"],
              [descD valueForKey:code],
              [priceD valueForKey:code]];
}


[self emailTxtFile:[NSString stringWithString:outStr]]; 

// This reaches end of method

topace
  • 1,791
  • 3
  • 17
  • 23
  • Looks good, although you can drop the `[NSArray arrayWithArray:...]`, `[NSDictionary dictionaryWithDictionary:...]` things. I.e. `NSArray *orderA = [ARAppDelegate sharedAppDelegate].orderArray;` will do fine and consume less memory. – mvds Mar 28 '11 at 16:20
  • you are retrieving `[dict valueForKey:@"CODE"]` three times per iteration, maybe once is enough? – mvds Mar 28 '11 at 16:41
  • ok let me try updating again. the reason i have been accessing data from app delegates a lot because it seems like a short cut to access data "globally". i know it's a very bad habit. – topace Mar 28 '11 at 16:42
  • sometimes it's a fine solution. But you could still do `[dict valueForKey:@"CODE"]` once. – mvds Mar 28 '11 at 16:43

1 Answers1

5

The problem is that in every iteration a new string object is formed. This consumes a lot of memory. One solution could be to use a local autoreleasepool, but that's rather complicated here.

You should use an NSMutableString, like:

NSMutableString *outStr = [[[NSMutableString alloc] init] autorelease];
for (int i = 0; i < count; i++) {
    NSDictionary *dict = [[ARAppDelegate sharedAppDelegate].orderArray objectAtIndex:i];
    [outStr appendFormat:@"%@,%@,%@,%@\n", 
          [dict valueForKey:@"CODE"], 
          [dict valueForKey:@"QTY"],
          [[ARAppDelegate sharedAppDelegate].descDict valueForKey:[dict valueForKey:@"CODE"]],
          [[ARAppDelegate sharedAppDelegate].priceDict valueForKey:[dict valueForKey:@"CODE"]]];
}

Then you can use outStr, just as if it was an NSString. As Tom points out in the comments, you could turn the NSMutableString into an NSString when you're finished, using:

NSString *result = [NSString stringWithString:outStr];

[outStr release]; // <-- add this line and remove the autorelease
                  //     from the outStr alloc/init line

making your code re-usable and easier to maintain.

mvds
  • 45,755
  • 8
  • 102
  • 111
  • Great answer. For more safety, its better to return a 'real' NSString, so realOut = [NSString stringWithString:outStr] will prevent you from making mistakes in the future. – Tom Andersen Mar 28 '11 at 15:29
  • see http://stackoverflow.com/questions/1616348/nsstring-stringwithstring-whats-the-point – Tom Andersen Mar 28 '11 at 15:30
  • good point. And for more speed, get `orderArray`, `descDict` and `priceDict` once, before you enter the loop. – mvds Mar 28 '11 at 15:32
  • Thank you all! I am testing the change and so far so good. After the above code I am calling a method [self emailTxtFile:[NSString stringWithString:outStr]]; Is that good enough? Should I release outStr inside that method when I finish using it? – topace Mar 28 '11 at 15:55
  • Also, @mvds, when you suggest get orderArray,descDict,priceDict once, do you mean creating a local version before the loop? Just want to make sure. – topace Mar 28 '11 at 15:57
  • Nononono don't do that! You only release what you create. Period. Elaborating: "create" means `alloc`/`copy`/`new`, "release" can be `release` or `autorelease`. We see *one* `alloc`, so we do *one* `autorelease`, within the same scope (i.e. function in this case) – mvds Mar 28 '11 at 15:59
  • @topace: yes indeed. You don't want to repeat things that always have the same end result, like getting the `sharedAppDelegate` and getting the `descDict` etc. – mvds Mar 28 '11 at 16:00
  • Thanks again, I am posting my final code above for everyone to see. – topace Mar 28 '11 at 16:07