In-loop releasing of objects does not free memory?

  • I am writing a Cocoa-program to reduce a large amount of measurement data.
    This involves reading in large files, like 6 MB each, in a loop over about 60 of them.
    I try to release as many temporary objects as possible whenever I'm finished with them. However, the memory is not freed, as can be seen using Activity monitor, and by a dramatic slowdown due to memory overflow.

    So the question is, how can I get the memory back? I need it for the next file(s)...

    Code looks like this:

    NSArray * files; // contains a list of filenames
    for(i=0;i<[files count]; i++)
    {
      NSString * dataFromFile = [[NSString alloc] initWithContentsOfFile: [files objectAtIndex: i]];
      < some processing >
      [dataFromFile release];
    }

    Autoreleased instances behave the same, i.e. no memory gets really freed.


    Thanks for your time,

    Arthur C.
  • On Feb 12, 2008, at 2:10 PM, Arthur C. wrote:

    > I am writing a Cocoa-program to reduce a large amount of measurement
    > data.
    > This involves reading in large files, like 6 MB each, in a loop over
    > about 60 of them.
    > I try to release as many temporary objects as possible whenever I'm
    > finished with them. However, the memory is not freed, as can be seen
    > using Activity monitor, and by a dramatic slowdown due to memory
    > overflow.
    >
    > So the question is, how can I get the memory back? I need it for the
    > next file(s)...
    >
    > Code looks like this:
    >
    > NSArray * files; // contains a list of filenames
    > for(i=0;i<[files count]; i++)
    > {
    > NSString * dataFromFile = [[NSString alloc] initWithContentsOfFile:
    > [files objectAtIndex: i]];
    > < some processing >
    > [dataFromFile release];
    > }
    >
    > Autoreleased instances behave the same, i.e. no memory gets really
    > freed.

    Two suggestions:

    * Use Instruments to figure out exactly what type of objects that
    you're "leaking" in your loop

    * Try inserting a local autorelease pool in the loop. You might be
    using some API that creates autoreleased objects.

    j o a r
  • On Feb 12, 2008, at 2:10 PM, Arthur C. wrote:

    > NSArray * files; // contains a list of filenames
    int c = [files count];
    /*
    >
    > for(i=0;i<[files count]; i++)
    */
    for (i= 0; i < c; i++)    // why do [files count] every iteration? since
    files is immutable
    >
    > {
    > NSString * dataFromFile = [[NSString alloc]
    > initWithContentsOfFile: [files objectAtIndex: i]];
    > < some processing >
    NSLog(@"retainCount = %d means memory will%@be freed", [dataFromFile
    retainCount], [dataFromFile retainCount] > 1 ? @" not " : @" ");
    >
    > [dataFromFile release];
    > }

    If you see any "will not be freed" messages in the console, then
    dataFromFile has been retained, but either not released  or
    autoreleased somewhere inside the loop.  Just one autorelease, instead
    of release will cause the memory to be freed when the autorelease pool
    is drained (way up in the main loop, if you haven't put one in place
    somewhere closer to this code).

    john
  • I think a better way to explain this would be, "try wrapping the body of
    the loop in an autorelease pool."

    John Terranova wrote:
    >
    > On Feb 12, 2008, at 2:10 PM, Arthur C. wrote:
    >
    >> NSArray * files; // contains a list of filenames
    > int c = [files count];
    > /*
    >>
    >> for(i=0;i<[files count]; i++)
    > */
    > for (i= 0; i < c; i++)    // why do [files count] every iteration?
    > since files is immutable
    >>
    >> {
    >> NSString * dataFromFile = [[NSString alloc] initWithContentsOfFile:
    >> [files objectAtIndex: i]];
    >> < some processing >
    > NSLog(@"retainCount = %d means memory will%@be freed", [dataFromFile
    > retainCount], [dataFromFile retainCount] > 1 ? @" not " : @" ");
    >>
    >> [dataFromFile release];
    >> }
    >
    > If you see any "will not be freed" messages in the console, then
    > dataFromFile has been retained, but either not released  or
    > autoreleased somewhere inside the loop.  Just one autorelease, instead
    > of release will cause the memory to be freed when the autorelease pool
    > is drained (way up in the main loop, if you haven't put one in place
    > somewhere closer to this code).
    >
    > john
  • I guess I should have been more explicit and said to, first, verify
    that retainCount > 1 with this type of NSLog().  Then, investigate why
    retainCount > 1.  If it is an autorelease, then "try wrapping the body
    of the loop in an autorelease pool," if that is your chosen solution.
    If there are no autoreleases (or not enough to account for the
    retainCount), then you must "find" your missing auto/release to patch
    the real leak.

    On Feb 12, 2008, at 4:33 PM, John Stiles wrote:

    > I think a better way to explain this would be, "try wrapping the
    > body of the loop in an autorelease pool."
    >
    > John Terranova wrote:
    >>
    >> On Feb 12, 2008, at 2:10 PM, Arthur C. wrote:
    >>
    >>> NSArray * files; // contains a list of filenames
    >> int c = [files count];
    >> /*
    >>>
    >>> for(i=0;i<[files count]; i++)
    >> */
    >> for (i= 0; i < c; i++)    // why do [files count] every iteration?
    >> since files is immutable
    >>>
    >>> {
    >>> NSString * dataFromFile = [[NSString alloc]
    >>> initWithContentsOfFile: [files objectAtIndex: i]];
    >>> < some processing >
    >> NSLog(@"retainCount = %d means memory will%@be freed",
    >> [dataFromFile retainCount], [dataFromFile retainCount] > 1 ? @" not
    >> " : @" ");
    >>>
    >>> [dataFromFile release];
    >>> }
    >>
    >> If you see any "will not be freed" messages in the console, then
    >> dataFromFile has been retained, but either not released  or
    >> autoreleased somewhere inside the loop.  Just one autorelease,
    >> instead of release will cause the memory to be freed when the
    >> autorelease pool is drained (way up in the main loop, if you
    >> haven't put one in place somewhere closer to this code).
    >>
    >> john
  • On Feb 12, 2008 5:10 PM, John Terranova <johnte...> wrote:
    > I guess I should have been more explicit and said to, first, verify
    > that retainCount > 1 with this type of NSLog().  Then, investigate why
    > retainCount > 1.  If it is an autorelease, then "try wrapping the body
    > of the loop in an autorelease pool," if that is your chosen solution.
    > If there are no autoreleases (or not enough to account for the
    > retainCount), then you must "find" your missing auto/release to patch
    > the real leak.

    You assume the object accumulating is one that you can query the
    retainCount of. It could be something created by the framework in
    response to -[NSString initWithContentsOfFile:].

    IMHO it seldom a good idea to query and log retain counts in this way
    (it can easily mislead) instead use tools like ObjectAlloc and now
    Instruments to understand what is taking place.

    -Shawn
  • On Feb 13, 2008 8:57 AM, Shawn Erickson <shawnce...> wrote:
    > On Feb 12, 2008 5:10 PM, John Terranova <johnte...> wrote:
    >> I guess I should have been more explicit and said to, first, verify
    >> that retainCount > 1 with this type of NSLog().  Then, investigate why
    >> retainCount > 1.  If it is an autorelease, then "try wrapping the body
    >> of the loop in an autorelease pool," if that is your chosen solution.
    >> If there are no autoreleases (or not enough to account for the
    >> retainCount), then you must "find" your missing auto/release to patch
    >> the real leak.
    >
    > You assume the object accumulating is one that you can query the
    > retainCount of. It could be something created by the framework in
    > response to -[NSString initWithContentsOfFile:].
    >
    > IMHO it seldom a good idea to query and log retain counts in this way
    > (it can easily mislead) instead use tools like ObjectAlloc and now
    > Instruments to understand what is taking place.

    I also meant to state that ideally you shouldn't make any assumptions
    about what the framework will do (it may change on you in the future).
    If you have a loop that logically has a good chance of causing a lot
    of objects and/or large objects to be created before unwinding back up
    to the runloop you should consider the use of a loop scoped
    autorelease pool (assuming you aren't using GC, if you are using GC
    consider using -[NSGarbageCollector collectIfNeeded]).

    <http://developer.apple.com/documentation/Cocoa/Conceptual/GarbageCollection
    /Articles/gcEssentials.html#//apple_ref/doc/uid/TP40002452-SW7
    >

    <http://developer.apple.com/documentation/Cocoa/Reference/NSGarbageCollector
    _class/Introduction/Introduction.html#//apple_ref/occ/instm/NSGarbageCollec
    tor/collectIfNeeded
    >

    -Shawn
  • [John Stiles:]

    > I think a better way to explain this would be, "try wrapping the body of > the loop in an autorelease pool."
    That did the job. It is simple:
    { // loop
      NSAutoreleasePool * loopPool = [[NSAutoreleasePool alloc] init];
      <do various things>
      [loopPool release];
    }

    Indeed, there were autoreleased objects created in the loop that I didn't consider.


    Thanks,

    Arthur C.
  • On Feb 14, 2008, at 1:43 AM, Arthur C. wrote:

    >> I think a better way to explain this would be, "try wrapping the
    >> body of > the loop in an autorelease pool."
    > That did the job. It is simple:
    > { // loop
    > NSAutoreleasePool * loopPool = [[NSAutoreleasePool alloc] init];
    > <do various things>
    > [loopPool release];
    > }
    >
    > Indeed, there were autoreleased objects created in the loop that I
    > didn't consider.

    In theory, a better way to do this would be:

    NSAutoreleasePool * loopPool = [[NSAutoreleasePool alloc] init];
    {  // loop
      <do various things>
      [loopPool drain];
    }

    But I have to admit, I recall having an issue where drain: didn't seem
    to work very well, so I ended up doing exactly what you did.  It
    didn't seem to be a performance issue for me, so I didn't take the
    time to investigate why drain: didn't work; it bugged the crap out of
    me to be creating an object like that every time through the loop, but
    I had more pressing things to deal with.

    Anyone else noticed this behavior with drain: or otherwise have an
    explanation?
  • On 14.02.2008, at 17:31, Randall Meadows wrote:
    > NSAutoreleasePool * loopPool = [[NSAutoreleasePool alloc] init];
    > {  // loop
    > <do various things>
    > [loopPool drain];
    > }

    Just for completeness' sake, this should really be:

    NSAutoreleasePool * loopPool = [[NSAutoreleasePool alloc] init];
    while( whatever ) // loop
    {
        // do stuff here.
        [loopPool drain];
    }
    [loopPool release];    // don't forget this.

    Cheers,
    -- Uli Kusterer
    "The Witnesses of TeachText are everywhere..."
    http://www.zathras.de
  • On Feb 14, 2008, at 12:49 PM, Uli Kusterer wrote:

    > Just for completeness' sake, this should really be:
    >
    > NSAutoreleasePool * loopPool = [[NSAutoreleasePool alloc] init];
    > while( whatever ) // loop
    > {
    > // do stuff here.
    > [loopPool drain];
    > }
    > [loopPool release];    // don't forget this.

    That'll cause a crash if GC isn't being used, since -drain is
    documented as doing the same thing as -release in non-GC applications.

    Nick Zitzmann
    <http://www.chronosnet.com/>
  • On Feb 14, 2008, at 1:12 PM, Nick Zitzmann wrote:

    > On Feb 14, 2008, at 12:49 PM, Uli Kusterer wrote:
    >
    >> Just for completeness' sake, this should really be:
    >>
    >> NSAutoreleasePool * loopPool = [[NSAutoreleasePool alloc] init];
    >> while( whatever ) // loop
    >> {
    >> // do stuff here.
    >> [loopPool drain];
    >> }
    >> [loopPool release];    // don't forget this.

    Yes, of course, my bad.

    > That'll cause a crash if GC isn't being used, since -drain is
    > documented as doing the same thing as -release in non-GC applications.

    I took that to mean that has the same effect on the *contents* of the
    pool (that is, releasing everything contained in the pool) but doesn't
    release the pool itself.  Am I being too liberal in my interpretation,
    or are you talking about something else?
  • Nick is referring to this bit from the -drain documentation:

    "In a garbage collected environment, triggers garbage collection if
    memory allocated since last collection is greater than the current
    threshold; otherwise behaves as release."

    When you aren't using GC, calling -drain on your AutoreleasePool
    object will act just like you called -release on it. As a result, in a
    loop construct like that, you'll crash if you aren't using GC.

    --
    m-s

    On 14 Feb, 2008, at 15:54, Randall Meadows wrote:

    >> That'll cause a crash if GC isn't being used, since -drain is
    >> documented as doing the same thing as -release in non-GC
    >> applications.
    >
    > I took that to mean that has the same effect on the *contents* of
    > the pool (that is, releasing everything contained in the pool) but
    > doesn't release the pool itself.  Am I being too liberal in my
    > interpretation, or are you talking about something else?
    > _____________
  • > When you aren't using GC, calling -drain on your AutoreleasePool
    > object will act just like you called -release on it. As a result, in
    > a loop construct like that, you'll crash if you aren't using GC.

    I've filed a bug on this and would encourage everyone to do the same.

    In my opinion -drain should do as the name implies; call -release on
    all the objects currently autorelease'd but not release the pool itself.

    Keith
  • Isn't it too late, though? If even one shipped program relies on the old
    behavior, it's carved in stone.

    Keith Duncan wrote:
    >> When you aren't using GC, calling -drain on your AutoreleasePool
    >> object will act just like you called -release on it. As a result, in
    >> a loop construct like that, you'll crash if you aren't using GC.
    >
    > I've filed a bug on this and would encourage everyone to do the same.
    >
    > In my opinion -drain should do as the name implies; call -release on
    > all the objects currently autorelease'd but not release the pool itself.
    >
    > Keith
  • On 14.02.2008, at 21:12, Nick Zitzmann wrote:

    >
    > On Feb 14, 2008, at 12:49 PM, Uli Kusterer wrote:
    >
    >> Just for completeness' sake, this should really be:
    >>
    >> NSAutoreleasePool * loopPool = [[NSAutoreleasePool alloc] init];
    >> while( whatever ) // loop
    >> {
    >> // do stuff here.
    >> [loopPool drain];
    >> }
    >> [loopPool release];    // don't forget this.
    >
    >
    > That'll cause a crash if GC isn't being used, since -drain is
    > documented as doing the same thing as -release in non-GC applications.

      Oh? Are you sure? Wasn't aware of that.

      But then the loopPool alloc/init statement should be inside the
    loop, not outside, because otherwise you'll get the crash the second
    time through the loop.

    Cheers,
    -- Uli Kusterer
    "The Witnesses of TeachText are everywhere..."
    http://www.zathras.de
  • --- Uli Kusterer <witness.of.teachtext...> wrote:

    >
    > On 14.02.2008, at 21:12, Nick Zitzmann wrote:
    >
    >>
    >> On Feb 14, 2008, at 12:49 PM, Uli Kusterer wrote:
    >> [SNIP]
    >> That'll cause a crash if GC isn't being used,
    > since -drain is
    >> documented as doing the same thing as -release in
    > non-GC applications.
    >
    >
    > Oh? Are you sure? Wasn't aware of that.

    If you want to confirm it yourself, create a
    Foundation tool and add "[pool drain]; [NSArray
    array];" anywhere in the boilerplate main file. You'll
    get the "Hey, create an autorelease pool, stupid!"
    warning when you run it.

    Cheers,
    Chuck

          ____________________________________________________________________________________
    Looking for last minute shopping deals?
    Find them fast with Yahoo! Search.  http://tools.search.yahoo.com/newsearch/category.php?category=shopping
  • On Feb 14, 2008, at 4:22 PM, Keith Duncan wrote:

    > I've filed a bug on this and would encourage everyone to do the same.
    >
    > In my opinion -drain should do as the name implies; call -release on
    > all the objects currently autorelease'd but not release the pool
    > itself.

    There's a limit to how much information can be inferred from the
    method name. Cocoa's generally good naming conventions cannot replace
    the API documentation.

    Consider why -drain exists, and why it is somewhat of a special case.

    In the usual case, alloc/copy/retain is balanced by release/
    autorelease. (Please see the API doc for a complete discussion of the
    object ownership and memory management policy.)

    The documentation for -drain says that in a non-GC app -drain, when
    sent to an NSAutoreleasePool, behaves identically to -release.

    Ugh. Special case. I hate special cases. I file bugs about special
    cases. So why do we have this special case?

    NSAutoreleasePool is already somewhat of a special case. You can't
    send -autorelease to an autorelease pool. (Can't in the sense that it
    will raise an exception. But it doesn't make sense anyway; if you were
    willing to autorelease the pool you didn't need the inner pool to
    begin with.)

    So why is there a -drain, that behaves identically to -release in a
    non-GC application?

    To answer that, we need to consider if autorelease pools are at all
    useful in GC code. Any place where you'd previously managed the high
    water mark of memory allocation using an NSAutoreleasePool, you could
    achieve the same effect by sending the collector a -collectIfNeeded
    message. But that would mean a) rewriting existing code and b) the new
    code would only work in GC mode.

    Yes, I realize that Apple only recommends GC for new development, and
    that porting memory managed code to GC often will require additional
    work beyond flipping the switch and recompiling.

    The second point is perhaps more salient - reworking the code to
    message the collector directly will only work in GC mode. Again,
    writing dual mode code is not typically recommended, but there are
    situations where it is necessary. (One of those situations is
    framework development at Apple itself.)

    So, leaving the inner autorelease pools around seems like a win. In GC
    mode they can message the collector, in non-GC mode they can do what
    they always did.

    But that creates a problem. The previous idiom was

    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    ...
    [pool release];

    But -release is a no-op in GC code. NSAutoreleasePool will never get
    the message. So we need a new message that we can send
    NSAutoreleasePool that it will actually get when running in GC mode.
    And for convenience, in non-GC mode it should do the same thing as -
    release always did, to provide a single, simple idiom for inner
    autorelease pools that will work in either GC or non-GC mode.

    Yes, strictly speaking, it is inconsistent. But also necessary; the
    alternatives would be uglier.

    [This is a fictional story. I wasn't privy to the design discussion
    for the -drain message. I'm just piecing together a reasonable
    explanation for why the way things are the way they are.]

    - Jim
  • On Feb 14, 2008, at 7:57 PM, Jim Correia wrote:

    > But -release is a no-op in GC code. NSAutoreleasePool will never get
    > the message. So we need a new message that we can send
    > NSAutoreleasePool that it will actually get when running in GC mode.
    > And for convenience, in non-GC mode it should do the same thing as -
    > release always did, to provide a single, simple idiom for inner
    > autorelease pools that will work in either GC or non-GC mode.
    >
    > Yes, strictly speaking, it is inconsistent. But also necessary; the
    > alternatives would be uglier.

    I don't know if I agree. Rather than repeatedly creating and
    destroying a local autorelease pool in a non-GC app, why not instead
    using "-drain" with the same type of semantics as in GC?

    ---------------------------------------------
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    for (id foo in foos)
    {
      // Stuff...
      [pool drain];
    }
    [pool release];
    ---------------------------------------------

    Much better IMO. The above would work fine in both GC and non-GC, and
    it doesn't suffer from the completely unexpected behaviour of having
    (drain == release) in non-GC.

    Now, this is all a moot point of course...  :-)

    j o a r
  • On Feb 14, 2008, at 11:36 PM, j o a r wrote:

    > On Feb 14, 2008, at 7:57 PM, Jim Correia wrote:
    >
    >> But -release is a no-op in GC code. NSAutoreleasePool will never
    >> get the message. So we need a new message that we can send
    >> NSAutoreleasePool that it will actually get when running in GC
    >> mode. And for convenience, in non-GC mode it should do the same
    >> thing as -release always did, to provide a single, simple idiom for
    >> inner autorelease pools that will work in either GC or non-GC mode.
    >>
    >> Yes, strictly speaking, it is inconsistent. But also necessary; the
    >> alternatives would be uglier.
    >
    > I don't know if I agree. Rather than repeatedly creating and
    > destroying a local autorelease pool in a non-GC app, why not instead
    > using "-drain" with the same type of semantics as in GC?

    Well, I was trying to provide a plausible explanation for why we ended
    up with -drain as a synonym for -release, not second guess the
    decision with the advantage of hindsight (and the disadvantage of not
    having all the facts before me.).

    > ---------------------------------------------
    > NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    > for (id foo in foos)
    > {
    > // Stuff...
    > [pool drain];
    > }
    > [pool release];
    > ---------------------------------------------
    >
    > Much better IMO. The above would work fine in both GC and non-GC,
    > and it doesn't suffer from the completely unexpected behaviour of
    > having (drain == release) in non-GC.

    This doesn't suffer from the problem where -drain is now a special
    case and is equivalent to release in a non-GC app. (But as I
    mentioned, NSAutorelease pool was already a bit of a special case
    anyway.)

    It has the disadvantage that it is a bigger change in the previous
    idiom for existing code which destroyed and created a new pool every
    loop iteration. (Or every N loop iterations.)

    (And there certainly could have been additional issues we haven't
    considered.)

    > Now, this is all a moot point of course...  :-)

    Indeed. -drain is what it is, and is documented that way :-)

    Jim
  • On Feb 15, 2008, at 7:41 AM, Jim Correia wrote:

    > On Feb 14, 2008, at 11:36 PM, j o a r wrote:
    >
    >> On Feb 14, 2008, at 7:57 PM, Jim Correia wrote:
    >>
    >>> But -release is a no-op in GC code. NSAutoreleasePool will never
    >>> get the message. So we need a new message that we can send
    >>> NSAutoreleasePool that it will actually get when running in GC
    >>> mode. And for convenience, in non-GC mode it should do the same
    >>> thing as -release always did, to provide a single, simple idiom
    >>> for inner autorelease pools that will work in either GC or non-GC
    >>> mode.
    >>>
    >>> Yes, strictly speaking, it is inconsistent. But also necessary;
    >>> the alternatives would be uglier.
    >>
    >> I don't know if I agree. Rather than repeatedly creating and
    >> destroying a local autorelease pool in a non-GC app, why not
    >> instead using "-drain" with the same type of semantics as in GC?
    >
    > Well, I was trying to provide a plausible explanation for why we
    > ended up with -drain as a synonym for -release, not second guess the
    > decision with the advantage of hindsight (and the disadvantage of
    > not having all the facts before me.).

    I remember the WWDC that was just after "drain" appeared in the
    headers, there was a comment to the effect that "yeah, we're going to
    work on GC, and that's what 'drain' is going to be used for, but in
    the mean time, don't use it because it really doesn't work like you'd
    expect it to".

    My memory colored this as "drain was not suppose to be synonymous with
    release, but that's how it ended up - sorry".  This, of course,
    neither confirms nor conflicts with the explanation ("release is a nop
    so we needed a special pseudo-release"), though the "Special
    Considerations" section of the documentation for -drain is consistent
    with that.

    And lets face it - creating and destroying an auto-release pool isn't
    that much more work/code/processor time than using a -drain call that
    works like you'd expect it to on non-GC...

    Perhaps filing a bug requesting a "-removeAllObjects" method be added
    to NSAutoreleasePool which is -drain under GC and is not -release
    under non-GC?

    Glenn Andreas                      <gandreas...>
      <http://www.gandreas.com/> wicked fun!
    quadrium | prime : build, mutate, evolve, animate : the next
    generation of fractal art
  • On Feb 14, 2008, at 10:57 PM, Jim Correia wrote:
    > But -release is a no-op in GC code. NSAutoreleasePool will never get
    > the message. So we need a new message that we can send
    > NSAutoreleasePool that it will actually get when running in GC mode.
    > And for convenience, in non-GC mode it should do the same thing as -
    > release always did, to provide a single, simple idiom for inner
    > autorelease pools that will work in either GC or non-GC mode.
    >
    > Yes, strictly speaking, it is inconsistent. But also necessary; the
    > alternatives would be uglier.

    I still don't understand.  Why does it matter that the release message
    to a NSAutoreleasePool would be a noop when running in GC mode?  Isn't
    autorelease also a noop?  In which case the whole NSAutoreleasePool is
    just a noop, since nothing would ever actually get added to it.  So I
    still don't understand the need for the drain method as it's currently
    defined.  Why couldn't you just send release to the pool in both GC
    and non-GC apps?  I'm not saying what you said is wrong, I'm sure
    it's me that's missing something.  Thanks,

    Adam
  • On Feb 15, 2008, at 11:27 AM, Adam P Jenkins wrote:

    > On Feb 14, 2008, at 10:57 PM, Jim Correia wrote:
    >> But -release is a no-op in GC code. NSAutoreleasePool will never
    >> get the message. So we need a new message that we can send
    >> NSAutoreleasePool that it will actually get when running in GC
    >> mode. And for convenience, in non-GC mode it should do the same
    >> thing as -release always did, to provide a single, simple idiom for
    >> inner autorelease pools that will work in either GC or non-GC mode.
    >>
    >> Yes, strictly speaking, it is inconsistent. But also necessary; the
    >> alternatives would be uglier.
    >
    > I still don't understand.  Why does it matter that the release
    > message to a NSAutoreleasePool would be a noop when running in GC
    > mode?  Isn't autorelease also a noop?  In which case the whole
    > NSAutoreleasePool is just a noop, since nothing would ever actually
    > get added to it.  So I still don't understand the need for the drain
    > method as it's currently defined.

    There aren't any autoreleased objects in a GC app in the traditional
    sense, but there will be uncollected garbage.

    -drain provides a single mechanism which you can use in a tight loop
    which generates lots of temporary objects to

    - in GC mode hints to the collector that it should collect some garbage
    - in non-GC mode, pop the autorelease pool

    which have similar effects on the high water mark of helping you keep
    down your high water mark.

    Clarifications or additional explanation from the obj-c GC designers
    welcome :-)

    Jim
  • On Feb 15, 2008, at 8:27 AM, Adam P Jenkins wrote:

    > I still don't understand.  Why does it matter that the release
    > message to a NSAutoreleasePool would be a noop when running in GC
    > mode?  Isn't autorelease also a noop?  In which case the whole
    > NSAutoreleasePool is just a noop, since nothing would ever actually
    > get added to it.  So I still don't understand the need for the drain
    > method as it's currently defined.  Why couldn't you just send
    > release to the pool in both GC and non-GC apps?  I'm not saying
    > what you said is wrong, I'm sure it's me that's missing something.
    > Thanks,

    Local autorelease pools are typically an optimization that we use when
    we know that we're going to generate a whole lot of temporary objects
    in a very short period of time, to keep the over all footprint of the
    application down. This improves the performance of your application,
    but also of the system as a whole.

    The same type of problem exists in GC apps of course, and while the
    collector might - and in a perfect world should - be smart enough to
    notice that you're generating temporary objects very quickly and to
    determine that it needs to respond to that, it might also be that it
    could benefit from a hint from the developer about this in much the
    same way, and in the same type of situations, as where you would use a
    local autorelease pool in non-GC.

    There is API for providing such a hint in a GC app (-
    [NSGarbageCollector collectIfNeeded]), but in code that runs in both
    GC and non-GC it might be convenient to not have to use both types of
    syntax and I think that's why "-drain" was introduced.

    j o a r
  • On Feb 15, 2008, at 12:15 PM, Jim Correia wrote:

    > On Feb 15, 2008, at 11:27 AM, Adam P Jenkins wrote:
    >> I still don't understand.  Why does it matter that the release
    >> message to a NSAutoreleasePool would be a noop when running in GC
    >> mode?  Isn't autorelease also a noop?  In which case the whole
    >> NSAutoreleasePool is just a noop, since nothing would ever actually
    >> get added to it.  So I still don't understand the need for the
    >> drain method as it's currently defined.
    >
    > There aren't any autoreleased objects in a GC app in the traditional
    > sense, but there will be uncollected garbage.
    >
    > -drain provides a single mechanism which you can use in a tight loop
    > which generates lots of temporary objects to
    >
    > - in GC mode hints to the collector that it should collect some
    > garbage
    > - in non-GC mode, pop the autorelease pool
    >
    > which have similar effects on the high water mark of helping you
    > keep down your high water mark.

    Ok, so the intended pattern for using drain when you want to write
    code which works in both GC and non-GC mode is something like:

    for (i = 0; i < 1000; i++) {
        NSAutoreleasePool *pool = [NSAutoreleasePool new];
        // do some work
      [pool drain];
    }

    where [pool drain] differs from [pool release] in that it does
    something useful in GC mode as well, whereas [pool release] would only
    do something useful in non-GC mode.  I think I understand now.  Thank
    you.
  • I hadn't considered the effect that changing the method would have on
    shipping apps - I was simply concerned that the function of the method
    doesn't match it's implied function IMHO.

    > for (i = 0; i < 1000; i++) {
    > NSAutoreleasePool *pool = [NSAutoreleasePool new];
    > // do some work
    > [pool drain];
    > }

    I like this way of doing things. I don't use -new anywhere else in my
    code but using it for NSAutoreleasePool in a GC app would highlight
    that the pool is a *special* case. This is the pattern I'll be using,
    thanks for that!

    Keith
  • On Feb 15, 2008, at 3:07 PM, Keith Duncan wrote:

    > I hadn't considered the effect that changing the method would have
    > on shipping apps - I was simply concerned that the function of the
    > method doesn't match it's implied function IMHO.
    >
    >> for (i = 0; i < 1000; i++) {
    >> NSAutoreleasePool *pool = [NSAutoreleasePool new];
    >> // do some work
    >> [pool drain];
    >> }
    >
    > I like this way of doing things. I don't use -new anywhere else in
    > my code but using it for NSAutoreleasePool in a GC app would
    > highlight that the pool is a *special* case. This is the pattern
    > I'll be using, thanks for that!

    As far as I understand, [Foo new] is exactly equivalent to [[Foo
    alloc] init], unless Foo has overridden +new to do something else.
    I've never understood why most example code shows [[Foo alloc] init]
    when [Foo new] is shorter, and I always just use +new.  Is there any
    reason not to do that?
  • On Feb 15, 2008, at 12:41 PM, Adam P Jenkins wrote:
    > As far as I understand, [Foo new] is exactly equivalent to [[Foo
    > alloc] init], unless Foo has overridden +new to do something else.
    > I've never understood why most example code shows [[Foo alloc] init]
    > when [Foo new] is shorter, and I always just use +new.  Is there any
    > reason not to do that?_______________________________________________

    Nope -- no reason at all.  Works fine and is 100% equivalent as you
    said.

    b.bum
  • > As far as I understand, [Foo new] is exactly equivalent to [[Foo
    > alloc] init]

    Correct.

    Chaining alloc and init is just the in vogue convention as far as I
    know. I've been told that -new used to be a popular way of doing it
    back in the days of NeXTStep.

    There is noting wrong with using it though.

    Keith
  • On 2/15/08 9:11 PM, Keith Duncan said:

    >> As far as I understand, [Foo new] is exactly equivalent to [[Foo
    >> alloc] init]
    >
    > Correct.
    >
    > Chaining alloc and init is just the in vogue convention as far as I
    > know. I've been told that -new used to be a popular way of doing it
    > back in the days of NeXTStep.
    >
    > There is noting wrong with using it though.

    Well, 'new' is a reserved word in C++, so if you use Cocoa's 'new'
    method you'll never be able to use Objective-C++ with that code.  It's
    kind of like how using 'id' as a variable name in C++ is a bad idea.

    --
    ____________________________________________________________
    Sean McBride, B. Eng                <sean...>
    Rogue Research                        www.rogue-research.com
    Mac Software Developer              Montréal, Québec, Canada