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 filenamesint 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
>NSLog(@"retainCount = %d means memory will%@be freed", [dataFromFile
> {
> NSString * dataFromFile = [[NSString alloc]
> initWithContentsOfFile: [files objectAtIndex: i]];
> < some processing >
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



