Re: Leaking Memory
Re: Leaking Memory
- Subject: Re: Leaking Memory
- From: Greg Titus <email@hidden>
- Date: Fri, 24 Jan 2003 23:04:27 -0800
On Friday, January 24, 2003, at 06:07 PM, Robert Goldsmith wrote:
The Application Kit creates a pool at the beginning of the event loop
and releases it at the end.
Ok, so in an event driven application, life is easier because you can
create thousands of objects and they will be cleared at the start of
the next event loop (how often is that, btw? any ideas?).
The next event is the next event. If you take 20 seconds in computation
during one mouse click, then the AppKit pool won't get cleared for 20
seconds. NSApplication's -run method looks something like:
while(1) {
pool = [[NSAutoreleasePool alloc] init];
// get event
// dispatch event to appropriate window
[pool release];
}
There's nothing magic going on here. All objects autoreleased while
dealing with a single event end up in that top-level autorelease pool
(unless you are creating subpools), and all are released when the event
dispatching code is finished.
How about NON-event based applications?
Then you are responsible for setting up your own autorelease pool, and
releasing and recreating it at whatever intervals are appropriate for
your application.
For instance, if I was to have a method that used 5 temporary objects
(created with convenience messages to the class). This method is
messaged 1 million times. I was a good programmer and set up a pool
when I started. Are the temporary objects released like an int would be
in a c function? Or are they placed in the autorelease pool? I assume
the latter.
Yep, they are all in the pool.
So how often should a pool be cleared; i.e. how quickly
does the system get bogged down vs how long does it take to release the
pool and create another?
This is a matter for profiling, probably.
And can you release a pool and create a new one (without a pool below
to catch objects) without errors or leaks?
Absolutely. If you write large loops with objects potentially
autoreleased inside the normal pattern would be to release and recreate
a pool every N times through the loop, where N is maybe 100 or so. Like
so:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
for (i = 0; i < 100000; i++) {
// do stuff
if ((i % 100) == 0) {
[pool release];
pool = [[NSAutoreleasePool alloc] init];
}
}
And, back to AppKit, if you create your own pool, is it this pool that
gets released at the end of each event loop?
It should, because you ought to match your allocation with a release
later in your code.
I assume not - but does
that mean that, if you are not careful, your pool ends up filled with
AppKit temporary items?
There is no difference. What is an "AppKit temporary item"? An object
is an object...
So creating your own pools as you would in
non-event driven apps is a very bad idea for AppKit apps.
No, it's fine, and often a good idea if you are creating a lot of
temporary objects while processing a single event. NSAutoreleasePools
are stacked. When you create a new one it goes on to the top of the
stack, and it is popped off when it is deallocated. When an object is
autoreleased, it is added to whichever pool is on top of the stack.
An object can be autoreleased multiple times (for which it should have
had a corresponding number of retains, of course), and these
autoreleases can be put into more than one pool. No problems. For
instance, you can do something like this:
pool1 = [[NSAutoreleasePool alloc] init];
foo = [NSString stringWithCString:"x"]; // here we create an object
that is autoreleased, so it goes in pool1, has retainCount=1
[foo retain]; // we retain it, retainCount=2
pool2 = [[NSAutoreleasePool alloc] init];
[foo autorelease]; // it goes into pool2, retainCount still = 2
[pool2 release]; // pool2 goes away, releasing everything in it. foo's
retainCount=1
[pool1 release]; // pool1 goes away, releasing everything in it. foo's
retainCount=0 and it is deallocated now
Questions, questions :)
As a C programmer, I do feel I need to know a little more about the way
things work so I can make judgments on how best to code a problem. It's
just the way I work. Of course, if this raises any questions for you as
well, then maybe your view on how memory management works is not so
complete after all. I never thought mine was.
I also suspect that, with so much talk of autorelease pools, many
newbies think that once they have that down pat, they don't need to
think about memory management further... a very incomplete and
inaccurate view indeed (if you want to be an efficient programmer, that
is).
The above isn't all either. In point of fact, an autorelease pool will
notice if it is being deallocated while it isn't at the top of the
stack and clean up every pool above it. So if we forgot our "[pool2
release]" line above, when pool1 was released it would also release
pool2 and every object inside. This is bad programming style and the
autorelease pool will log a message to warn you.
We also haven't talked about multithreaded programming. It turns out
that these stacks of autorelease pools are actually one per thread
instead of being a single global stack. When you call -autorelease on
an object, it is placed in the top pool on the stack of the thread that
executed that method call. So you can count on the fact that if you
autorelease an object in a worker thread that it won't actually go away
until the pool for that thread is released, even if other threads are
doing other things (your main thread handling multiple events in the
meanwhile, for example).
All of this extra complication is interesting, and can be handy
information indeed for writing a program efficiently, but the whole
point of the setup is to make the rules for program _correctness_ very
easy to remember and understand:
* If you -alloc, -retain, or -copy something, then eventually you
should -release or -autorelease it.
* If you -release it, the object may be deallocated immediately (other
objects may retain it, but you shouldn't count on that).
* If you -autorelease it, the object will stay around at least until
the autorelease pool for that thread is released. In an AppKit app,
that will be (unless you've created additional pools that are released
sooner) the end of the current event.
Which brings up one final code pattern to be aware of. If you create
your own pool, make sure objects that you want to hang around after the
pool is gone are retained and then autoreleased again outside the pool:
pool = [[NSAutoreleasePool alloc] init];
foo = [bar someMethodThatDoesWhoKnowsWhat];
[foo retain]; // make sure foo lasts beyond the pool, even after other
temporary objects are deallocated
[pool release];
[foo autorelease]; // matching the retain above, but now foo lasts
longer
The most common occurrence of this pattern is when you are returning
"foo" to your caller.
Hope this additional detail is helpful.
- Greg
_______________________________________________
cocoa-dev mailing list | email@hidden
Help/Unsubscribe/Archives:
http://www.lists.apple.com/mailman/listinfo/cocoa-dev
Do not post admin requests to the list. They will be ignored.