Re: NSAutoreleasePool: how does it really work?
Re: NSAutoreleasePool: how does it really work?
- Subject: Re: NSAutoreleasePool: how does it really work?
- From: Phill Kelley <email@hidden>
- Date: Wed, 19 Mar 2003 00:29:25 +1100
G'Day Lorenzo!
>
I don't understand yet how does NSAutoreleasePool really work.
>
At the end of the following loop, the "top" command tells me that the Rsize
>
of my application doesn't decrease anymore. Instead I expect that it should
>
return to the initial value because I remove all the objects from the
>
xArray. I get lack of memory all the time. I know that "addObject" retains
>
the object just added, but, how to free up the memory at the end?
>
This is my spin on how it all works (and I'd welcome any corrections and
clarifications).
Firstly:
1. Each object you create has a retain count.
2. Sending a "retain" message to an object causes its retain count to be
incremented by one.
3. Sending a "release" message to an object causes its retain count to
be decremented by one.
4. When a "release" message causes the retain count of an object to be
decremented to zero, the memory associated with the object is freed.
5. Sending an "autorelease" message to an object does nothing to the
retain count. It merely adds the object to the autorelease pool. When
the autorelease pool is released, the objects in the pool are sent
release messages at that time.
Secondly, there are two ways in which an object can be created:
1. Factory methods (such as NSMutableArray's arrayWithCapacity) return
the object with a retain count of one, but also autoreleased.
Therefore, if you want such an object to hang around, you need to
send it a retain message, plus either a release or an autorelease
when you no longer need it.
2. The alloc/init sequence (or other initializer variants like NSString's
initWithFormat) returns the object with a retain count of one. Therefore,
you always need to send such an object a release or autorelease message
when you no longer need it.
Thirdly, how the autorelease pool is managed is a function of what kind of
application you are writing:
1. For a cocoa application, an autorelease pool is created at the
beginning of each event loop, and released at the end of that
event loop. You don't need to think about this.
2. For any other kind of application, you need to manage it yourself.
Personally, I have found a two-level approach to be effective.
For example:
int main (int argc, const char * argv[]) {
NSAutoreleasePool * applicationPool = [[NSAutoreleasePool alloc] init];
// initialization code goes here. Any objects auto-released during
// this phase go to applicationPool but will be available for the
// life of the application, as you would hope and expect.
// enter main loop
while (running) {
// instantiate a per-loop autorelease pool
NSAutoreleasePool * loopPool = [[NSAutoreleasePool alloc] init];
// do some useful work. Any objects auto-released during this
// pass through the loop go to loopPool, will only be available
// during this pass through the loop, and will disappear at
// the end of the loop.
// release the per-loop pool
[loopPool release];
}
// release the application pool
[applicationPool release];
return 0;
}
Finally, although this repeats some of what I wrote above, you need to keep
these rules in mind:
1. An object created by a factory method is autoreleased. If you do nothing
else, it will disappear automatically when its autorelease pool is
released. For a cocoa app, that's at the end of the current event loop.
2. An object created via alloc/init is not auto-released. You must balance
each alloc/init with either a release or an autorelease.
3. For each and every object to which you send a retain, you must also send
a matching release or autorelease.
>
===============================
>
- (void)MyLoop
>
{
>
int i;
>
NSMutableArray * xArray = [NSMutableArray arrayWithCapacity:0];
xArray is created via a factory method and is, therefore, auto-released. An
autorelease pool which exists outside the scope of MyLoop will have taken
note of the autorelease message sent to xArray during its creation.
>
for(i = 0; i < 500000; i++){
>
NSAutoreleasePool *myPool = [[NSAutoreleasePool alloc] init];
now you've created an autorelease pool that will only exist inside the
scope of the for-loop, and for a single iteration of the for-loop.
>
NSString *sourceItem = [[NSString alloc]
>
initWithFormat:@"Item number %d", i];
sourceItem is allocated with a retain-count of one.
>
[sourceItem autorelease];
myPool now knows about sourceItem and will send it a release message when
myPool is autoreleased. The retain count of sourceItem is still 1.
>
[xArray addObject:sourceItem];
sourceItem is added to xArray which sends it a retain message. Its retain
count is now 2.
>
[myPool release];
myPool sends each instance of sourceItem a release message. The retain
count of each instance will now be 1. None of the items will actually be
released because xArray still "owns" them.
>
}
>
[xArray removeAllObjects];
xArray sends each object a release message, causing the retain count for
all sourceItem objects to become zero, so all sourceItem objects are
destroyed. You can prove to yourself that this is occurring by overriding
the dealloc method and writing a message.
xArray, however, still exists because the autorelease pool to which it was
allocated has not been released.
In my own testing, the objects are sent dealloc messages at exactly the
times I expect. Therefore, either you are doing something different or
Rsize isn't telling you what you think it is telling you.
On that subject, my understanding of Rsize is that it reports the number of
real pages of memory currently allocated to the process. I may be wrong but
I doubt that releasing objects within an application will have any
correlation with the amount of real memory allocated to that application. I
doubt that the number will reduce until such time as the page manager needs
to recover some real memory and pages something out.
>
}
>
===============================
>
>
>
>
If I use only:
>
===============================
>
- (void)MyLoop
>
{
>
int i;
>
xArray = [NSMutableArray arrayWithCapacity:0];
>
for(i = 0; i < 500000; i++){
>
[xArray addObject:@"abc"];
Here, you are not actually creating an object each time you pass through
the loop. You are merely adding a reference to the same static @"abc"
object. During the addObject, xArray sends the retain message and the
removeAllObjects below sends the corresponding release message. However,
@"abc" will continue to exist for the life of the application. Objects like
@"abc" have a retain count of -1. You can send any number of retain,
release and autorelease messages to such objects and the retain count
remains at -1. So, in effect, you aren't creating anything and aren't
destroying anything.
>
}
>
[xArray removeAllObjects];
The same caveats apply to xObject as in the first example. xObject is
created and autoreleased by the factory method; the autorelease pool exists
outside the scope of this routine; xObject won't be released until its
autorelease pool is released.
>
}
>
===============================
>
it works well: at the end of the loop, the Rsize returns to the initial
>
value.
Almost certainly due to the fact that you aren't allocating a lot of objects.
>
Where can I learn more about NSAutoreleasePool? I already read the
>
documentation, but it seems to be not enough. And what did I do wrong?
I hope this helps.
Regards, PK
_______________________________________________
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.