Re: A few questions about Memory Management
Re: A few questions about Memory Management
- Subject: Re: A few questions about Memory Management
- From: James Spencer <email@hidden>
- Date: Sat, 12 Feb 2005 10:06:03 -0600
On Feb 11, 2005, at 7:56 PM, Adrian R. Foltyn wrote:
Hi there,
I have a couple of questions about Memory Management and hope that
somebody finds time to help me out here.
Other people have answered your questions but there are a couple of
things that were omitted that might help:
----------------------------------------------------------------
If I dealloc a NSArray holding other objects that were previously
alloced and inited the objects that were contained in the array are
neither released nor dealloced?
----------------------------------------------------------------
As has been pointed out, you should not delloc anything; you call
release and if the retain count drops to zero, the system will dealloc
the object. And has also been pointed out, when you add objects to an
array, they get are retained by the array. They are then released when
the array is dealloced. The additional point here is that if you
created the objects through alloc and init the release message sent
when the array is dealloced will NOT cause the individual objects to be
dealloced unless you have another release somewhere. Thus, if you do
something like the following:
NSMyObject *myObject = [[NSMyObject alloc] init];
NSArray *theArray = [NSArray arrayWithObject:myObject];
then you also need to follow this up with something like:
[myObject release};
or you will leak. In the above code, myObject essentially gets two
retains, one when it is alloced and one when it is added to the array.
If you don't do an explicit release, then the only release it will ever
get is when the array is deallocated and because the retain count would
still be one, the object would not get released.
Note also that, as discussed below, theArray, having been created with
a convenience method and not will alloc, is an autoreleased object and
will go away when the autorelease pool is destroyed, i.e. the next time
through the event loop assuming you haven't created your own
autorelease pool.
If I understood correctly onelinecoding in Cocoa is bad if you care
about how much memory your program uses. As an example:
NSString *dreamWorld = [[[NSString
initWithContentsOfFile:@"/path/to/realWorld.txt"]
componentsSeparatedByString:@"hate"]
componentsJoinedByString:@"love"];
With the above line I will never have the chance to send a release
message to the NSString instance that stores the contents of the file
and the NSArray instance that stores the delimited text as I don't
have any pointers in place that reference to them. Neither the end of
the method this code is executed in nor the deallocation of the object
this method is used in will result in removing the NSString and
NSArray instance from memory.
...
This won't compile as the NSString class does not have an
initWithContentsOfFile class method. As has been pointed out, you
would have to either alloc a string or call a class convenience method.
Having said that, the only string that you would have to worry about
explictly releasing in the above code would be the string you create
with alloc. Unfortunately, this wold leak Thus, if you modified the
above to be:
NSString *dreamWorld = [[[[NSString alloc]
initWithContentsOfFile:@"/path/to/realWorld.txt"]
componentsSeparatedByString:@"hate"]
componentsJoinedByString:@"love"];
would leak the string for which you have no reference but which was
created by alloc. Your choices are to either do what you noticed and
keep a reference to that string so you can release it:
NSString *realWorld = [NSString
initWithContentsOfFile:@"/path/to/realWorld.txt"];
NSString *dreamWorld = [[realWorld componentsSeparatedByString:@"hate"]
componentsJoinedByString:@"love"];
[realWorld release];
As with the array example, dreamWorld here is an autoreleased object so
you don't have to release it; it will be released the next time the
autorelease pool is destroyed. Actually you may have to retain it if
you want to keep it around outside the local scope.
----------------------------------------------------------------
In the documentation for NSArray there is this example code:
NSArray *pathArray = [NSArray arrayWithObjects:@"System", @"Library",
nil];
NSLog("The path is /%@.\n", [pathArray componentsJoinedByString:@"/"]);
Is it impossible to dealloc the resulting NSString instance of the
componentsJoinedByString: call?
First of all, there is what I assume is a typo in the above: NSLog
expects an NSString so the second line needs to be:
NSLog(@"The path is /%@.\n", [pathArray componentsJoinedByString:@"/"]);
More to the point, and this deserves stressing so it'll get repeated
here, you don't dealloc anything. You release the objects, reducing
the retain count by one. When the retain count gets to zero, the
system will dealloc the object.
Having said that, you are correct that you can't send ANY messages to
what is essentially a temporary created by componentsJoinedByString.
However, you don't need to: ANY object that is created by ANY means
other than alloc or copy (or their obvious variations) are provided to
you autoreleased. Essentially this means that the object already has
been released but the actual release message has been deferred so you
can actually do something with the object before it is destroyed (in
this case pass it to NSLog()). Later, when the autorelease pool is
destroyed, an actual release message will be sent to the string object
and, assuming that it's retain count drops to zero at that point (which
in this case it necessarily will as in this context the only
opportunity to effect the retain count was when the temporary string
was created in the first place.)
Would it be safe to say that example code in the documentation is not
a good place to study memory management as it tries to demonstrate a
concept in brief?
My experience is that most of the example code in the documentation
does NOT explictly leak; it may not contain all the explicit release or
autorelease calls but the code presented won't typically leave zombie
objects that can't be accessed but nevertheless need releasing. The
example code above does NOT leak: pathArray is autoreleased as are all
temporary objects. (Note that there are a lot more such objects in the
above than simply the string created by componentsJoinedByString: each
of the explicit string objects are autoreleased too: @"System",
@"Library" and "The path is /%@.\n" too. ) That said, it is probably
wise to use code snippets (as versus full blown sample applications)
for their announced purpose and be wary of using them for unannounced
purposes such as in this example memory management.
If I would completely ignore memory management while writing a simple
command line tool does the system free the objects in memory after the
tool exits?
What happens to the memory if it crashes?
When a process, whether a simple command line tool or a full blown
AppKit application, terminates whether regularly or by crashing, the
memory that was allocated is all recovered. In the meantime though if
you haven't done anything regarding memory managment, even a simple
command line tool will be throwing error messages even if they don't
actually crash the program. For example, if you put the above sample
code (the pathArray and NSLog() code) in a simple Cocoa foundation tool
that doesn't create an NSApplication and which doesn't explictly create
it's own autorelease pool, it will run and the run log will display the
string as expected but you will also receive error messages such as the
following:
2005-02-12 09:52:46.125 TestFoundationTool[3991] ***
_NSAutoreleaseNoPool(): Object 0x308810 of class NSCFString
autoreleased with no pool in place - just leaking
James P. Spencer
Rochester, MN
email@hidden
"Badges?? We don't need no stinkin badges!"
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Cocoa-dev mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden