stringWithFormat within a For loop

Discussion in 'iOS Programming' started by animefx, Jul 16, 2012.

  1. animefx, Jul 16, 2012
    Last edited: Jul 16, 2012

    animefx macrumors regular

    Joined:
    May 10, 2005
    Location:
    Illinois
    #1
    I've read that stringWithFormat and (alloc) initWithString are basically the same except obviously you have to [release] the alloc init yourself and stringWithFormat is retained.

    My problem however is how to use these in a for loop effectively. I could do it this way...

    Code:
    for (int x = 1, blah, blah) {
    do something
    blah blah stringWithFormat
    do something
    }
    
    other code
    However from what I understand it's using a a lot of memory because esentially each time through the loop its increasing the retain count.

    If I did it this way it seems like it is equally if not more inefficient:

    Code:
    for (int x = 1, blah, blah) {
    do something
    blah blah alloc initwithformat blah blah
    do something
    [release mystring];
    }
    
    other code
    so i'm alloc init everytime through the for loop and releasing at the end and repeating... this doesn't seem like good coding practice to me. maybe there would potential for memory leaks, although it seems like there is more potential for memory leaks with the 1st code example than the 2nd?

    any thoughts on this would be helpful. I know people would suggest alloc/init before the for loop but this wouldn't be helpful because the for loop is what will populate the string with it's data.
     
  2. chown33, Jul 16, 2012
    Last edited: Jul 16, 2012

    chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #2
    Look at NSMutableString. Alloc and init one NSMutableString before the loop. Append things directly to it in the loop. When the loop ends, you have one NSMutableString that holds everything you appended (or deleted, or whatever).

    Conceptually, Cocoa has a fair number of classes that are immutable. NSString, NSArray, NSDictionary, etc. These are not intended to be use for building things incrementally, which is what a loop does.

    When building things, or making things that need to be inherently mutable (changeable, modifiable), you should use the mutable subclass: NSMutableString, NSMutableArray, etc.

    Read the String Programming Guide for how to use strings sensibly.

    Also read Cocoa Fundamentals Guide, which explains Cocoa's overall design, like the mutable/immutable relationships.


    EDIT
    It seems apparent that you're not following steps in a book or tutorial. I can't tell whether you're using a book or online reference to look things up, but I'd guess not. This is a poor way to learn.

    The reason books and tutorials exist is because they present information in sequence, and show the correct things to do at each step of the sequence. If you don't see that sequence, or aren't looking things up in a reference, you'll never learn that there are much simpler approaches to solving problems (such as the mutable subclasses). Do yourself a favor, and follow an actual book, in sequence, without skipping around. At a minimum, go to one of the books you have and look up NSMutableString in the index.
     
  3. Duncan C macrumors 6502a

    Duncan C

    Joined:
    Jan 21, 2008
    Location:
    Northern Virginia
    #3

    You need to be clear on your terminology.

    alloc/init creates an object with a retain count of 1. You own such an object, and are responsible for releasing it when you are done with it.

    stringWithFormat is what Apple calls a "convenience method." It creates an autoreleased object that you do not own. If you don't do anything, the object will get released on the next pass through the event loop.

    autoreleased objects are useful for temporary objects that you create and just forget about when you are done with them. The system cleans them up and everything is good.

    However, if you are in a for loop that runs thousands of times, all those temporary objects accumulate in memory, and only get deallocated once the for loop finishes. You wind up with a whole bunch of objects that are slated for release when the autorelease pool is drained once your code returns.

    This can cause you to run out of memory before the for loop finishes. However, you have to create very big objects, and a lot of them, for this to be a problem. As an example, if you create 10,000 strings that are 5000 characters long, that's 50 megabytes of data. It's a lot, but unless your program has lots of other in-memory content (or you are running on a really old device like an iPhone 3 (not a 3g), you can probably get away with creating 50 megabytes of temporary data in a for loop.

    If you are creating tons of temporary objects, then yes, using initWithFormat/do something/release is the way to go. That way, you create an object, use it, release it, and then move on to the next object each time. The temporary objects do not accumulate in memory.

    As the other poster said, you can sometimes refactor your code to use a mutable string instead.

    However, you have to be careful. There is lots of code that creates temporary strings that you aren't even aware of, and sometimes you need to use system calls that return temporary strings and can't avoid those calls.

    Another alternative is autorelease pools. You could create and drain autorelease pools inside your for loop. For iOS 5 or later, that looks like this:


    Code:
    for statement
    {
      @autoreleasepool
      {
         //code that creates temporary objects
      }
    }
    
    In that code, the autorelease pool gets drained each time you leave the @autoreleasepool scope (on each pass through the for loop.)

    For iOS < 5.0, you have to alloc/init and drain autorelease pools, which is slower than the new @autoreleasepool compiler directive. I usually write code that does it every N iterations through my for loop to improve efficiency:

    Code:
    NSAutoReleasePool *pool = [[NSAutoReleasePool alloc] init;
    for (i = 0; i<50000;i++)
    {
      //code that creates temporary objects
      if ((i+1) % 500 == 0)
      {
        [pool drain];
        pool = [[NSAutoReleasePool alloc] init;
      }
    }
    [pool drain];
    
    Using autorelease pools has the advantage that they release EVERY temporary object your code creates, so you can write code that uses system functions, and code that creates tons of temporary objects, and they get cleaned up automatically.


    Regards,

    Duncan Champney
    WareTo
     

Share This Page