I have a problem with the memory management in Objective-C. Say I have a method that allocates an object and stores the reference to this object as a member of the class. If I run through the same function a second time, I need to release this first object before creating a new one to replace it. Supposing that the first line of the function is:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
This means that a different auto-release pool will be in place. The code to allocate the object is as follows:
if (m_object != nil)
[m_object release];
m_object = [[MyClass alloc] init];
[m_object retain];
The problem is that the program crashes when running the last line of the method:
[pool release];
What am I doing wrong ? How can I fix this ?
Regards
Alan
First get a general understanding of the objective c memory management. You are confusing a lot of different things here. For example you don't have to retain the m_object since alloc already sets up the retain count with 1. Also normally you dont release you AutoReleasePool when you release a object. Like I said look up the documentation for memory management (pretty good actually).
Autorelease pools handle objects that have been specifically autoreleased
Example:
[object autorelease];
You have to have at least one NSAutoreleasePool in your program in case some code attempts to autorelease an object. If this is your only NSAutoreleasePool then releasing the pool maybe causing your problems.
Related
Hey, I've got a quick autorelease question. I understand basically how it works, but I was wondering if the following would create a memory leak.
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
NSString* string = [[[NSString alloc] init] autorelease];
[[string retain] autorelease];
[pool drain];
Will the string be sent two release messages?
Think of the your interaction with the retain count of an object purely as a delta.
If you increase it by one, you need to decrease it by one for that object to potentially be released.
So -- yes -- you increased it by one twice and decreased it by one twice in that code. Two releases on drain will be the result.
That will not create a memory leak and you are correct, it will be sent two release messages from the auto release pool. As long as you have one release/autorelease for every alloc/retain/copy you should not be getting any leaks.
Consider the following program:
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// Insert code here...
NSLog(#"Programming is Fun !");
[pool drain];
return 0;
}
I don't understand why pool is needed there as the same program can be also written as this:
int main (int argc, const char * argv[]) {
NSLog(#"Programming is Fun !");
return 0;
}
What is a purpose of using an auto release pool? Why and when do we need them? Are they mandatory in every objective C program?
If i don't want to auto release any object, do I also need to use an Auto Release pool ?
NSObject contains a neat function called autorelease. This means all objects in Objective-C contain this function.
This function inserts self into the autorelease pool, delaying the call to object's release function until the autorelease pool is deallocated. Most internal APIs use an autorelease pool, and beside the one located in main(), there is one allocated and deallocated in UIKit's main loop in every pass.
In short: it's the queue for delayed decrement of reference counter.
Example on where autorelease is hidden:
[NSString stringWithUTF8String:"some string"];
This object is allocated, and autorelease is called on it. How would you use it yourself?
MyObject *obj = [[[MyClass alloc] init] autorelease];
Why is this good? When you return this object, the calling function does not need to take care to release this object, and optionally it can retain it (but doesn't have to).
To expand and clarify four years later:
While UIKit and AppKit create and drain an NSAutoreleasePool during the course of their main runloop, in your non-GUI program you need to create it yourself. Various code expects having an NSAutoreleasePool present, and since you didn't initialize a GUI framework nor you use one, there is no code that will magically create it for you.
While NSLog() and a constant NSString in your example don't require a pool, even the trivial [NSMutableArray array] does, given that it can actually be interpreted as [[[NSMutableArray alloc] init] autorelease].
But if i don't want to auto release any object, then also do i need to use Auto Release pool ??
Also note that the Cocoa library uses autorelease extensively. So, even if you think you don't use the pool in your code, you need to prepare the pool.
I have found reason...
"If a pool is not available, autoreleased objects do not get released and you leak memory. In this situation, your program will typically log suitable warning messages."
You typically do not have to create an autorelease pool block yourself, or even see the code that is used to create one. three occasions when you might use your own autorelease pool blocks,
If you are writing a program that is not based on a UI framework,
such as a command-line tool.
If you write a loop that creates many temporary objects - You may use
an autorelease pool block inside the loop to dispose of those objects
before the next iteration. Using an autorelease pool block in the
loop helps to reduce the maximum memory footprint of the application.
If you spawn a secondary thread - You must create your own
autorelease pool block as soon as the thread begins executing;
otherwise, your application will leak objects.
A general introduction can be found on Apple's website:
Autorelease Pools and Ownership and Disposal
Check out the NSAutoreleasePool documentation
I am calling a function repeatedly with a loop, and the loop runs inside of a thread. The thread has an autorelease pool.
I have the following code inside that function:
NSXMLDocument* undoXML;
NSData* undoData = [NSData dataWithContentsOfFile:undoFilePath];
undoXML = [[NSXMLDocument alloc] initWithData:undoData options:NSXMLDocumentTidyXML error:&err];
NSData* undoData2;
undoData2 = [undoXML XMLData];
[undoData2 release];
[undoXML release];
I'm getting the following strange results:
My program is leaking memory every time this function is called by the loop.
When I add the following code to the function:
NSData* undoData3;
undoData3 = [undoXML XMLData];
[undoData3 release];
My program leaks even more memory than before.
I'm really confused and I badly need help figuring out what's going on. Maybe my autorelease pool isn't working correctly? Why is this happening?
Are you sure it is leaking? Or is it simply growing in size?
What does your loop look like and how is the autorelease pool integrated into it?
The autorelease pool must be inside the loop or your loop will just build up tons of memory over time. That the leaks instrument doesn't show leaks indicates that you have violated the memory management rules or your loop is incorrect.
Try running Instruments on your project w/ the leak detection settings. This should identify exactly where your leak is occurring (even in the system libraries).
Run -> Run With Performance Tool -> Leaks
undoData should be preset to be autoreleased (according to naming convention dataWithContentsOfFile: returns an autoreleased object). But unless you have your own autorelease pool, nothing that is set to autorelease will actually be deallocated until the active pool is drained (i.e. sometime after your function returns).
Your thread may have its own autorelease pool, but unless you are creating one inside your own function, nothing will be deallocated until after your function exits.
If you want to trigger the draining of autoreleased objects in the middle of a function (say once per loop), you need to manage your own autorelease pool.
while(looping) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// do stuff that produces autoreleased objects
[pool drain];
}
Also, per cobbal's comment on your question, it looks like you should not be releasing undoData2 (or undoData3). Based on the naming convention -[NSXMLDocument XMLData] should be returning an autoreleased object.
If you have access to the source code of the NSData class you should look at what objects are being instantiated when undoData3 is created. I say this because you create the object and immediately destroy it. The issue must be that memory is being allocated inside the class but not being deallocated in it's destructor.
I've searched far and wide for its meaning. My guess is that I somehow have a corrupt stack. I get
tiny _ free_ list_ add_ ptr
on the 16th call of the line that says:
NSDateFormatter *theFormatter = [[NSDateFormatter alloc] init];
What is the cause of the problem? Am I correct in thinking that I have a corrupt stack?
- (NSString *)formatDate:(NSString *)uglyDate withFormat:(NSString *)theFormat {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSDateFormatter *theFormatter = [[NSDateFormatter alloc] init];
[theFormatter setDateFormat:theFormat];
NSDate *realDateUgly = [NSDate dateWithNaturalLanguageString:uglyDate];
if (realDateUgly == nil)
realDateUgly = [NSDate dateWithString:uglyDate];
NSString *prettyDate = [[NSString alloc] initWithString:[theFormatter stringFromDate:realDateUgly]];
[pool drain];
[pool release];
[theFormatter release];
return prettyDate;
}
I doubt you need a pool here.
You're over-releasing the pool. drain is the same as release in non-GC code. (release is just as redundant in GC code, because then it's a no-op.)
You're leaking prettyDate. You're supposed to autorelease it. (Of course, that won't work with the pool around it, which is a good reason to kill off that pool.)
Once you review the Memory Management Programming Guide for Cocoa and fix your memory-management problems, you should either find the problem fixed or at least be better able to track it down.
If you do still have the problem after you fix your memory management, please edit your question to include the complete stack trace.
I assume you're crashing in tiny_free_list_add_ptr. If so, tiny_free_list_add_ptr sounds like a function that a malloc implementation would use to keep track of memory blocks on the heap. If the heap is corrupted, I would expect a function like this to crash.
You're probably over releasing something (like the auto release pool that peter pointed out) here or in another method.
You should try running with the NSZombiesEnabled environment variable set. See http://developer.apple.com/technotes/tn2004/tn2124.html#SECFOUNDATION
I'd be willing to bet the problem is this:
[pool drain];
[pool release];
In a non-GC app, drain 'behaves' like release. 'Behaves' is the word used in the documentation, but the documentation is a bit ambiguous when you need to be pedantically precise as to exactly what happens when -drain is called. To me, at least, 'behaves' allows for a little bit of wiggle room, especially when compared to 'drain is exactly the same as release', which leaves a lot less room for interpretation.
The reason I bring this up is 'what happens to the autorelease pool after -drain is called?' I could not find a satisfactory answer in the documentation to this question. In different places, the documentation implies that when running in GC mode, -drain behaves as a 'hint to the GC system' and calls objc_collect_if_needed(). I could find nothing that explicitly says that when running in GC mode, an autorelease pool that has been sent a -drain message is 'no longer valid' (ie, something along the lines of behaving as if it was sent a release message). Nothing I could find in the documentation seems to expressly forbid sending an instantiated NSAutoreleasePool object a -drain message multiple times when running under GC.
The closest thing I could find was near the top of the NSAutoreleasePool class documentation: 'draining a pool ultimately has the effect of deallocating it'. This does little to help us here, though. The context from which this was taken is not terribly clear as to whether or not this applies to GC or non-GC mode. In any case, it is qualified with 'ultimately', which by pedantic dictionary definition means 'not now, but eventually'. Without the 'ultimately' qualification it's unambiguous as to whether or not the the instantiated autorelease pool object has been deallocated, and by induction, that sending additional messages to that pointer will result in undefined behavior.
So, since I can't point to anything authoritative to back this up, it's my opinion that -drain, in non-GC mode, behaves 'exactly' like -release (most likely implemented internally as [self release]). If this is true, you have 'over released' the NSAutoreleasePool object, in which case the problem will go away if you comment out one of the two statements.
Here is the gist of some code I'm writing. I'm concerned that I am not properly addressing the retain/release issues with the array class method on NSMutableArray. Is the following actually leaking memory?
for(a while) {
// do stuff
NSMutableArray *a = nil;
// do stuff
if (!a) {
a = [NSMutableArray array];
}
} // for(a while)
You wouldn't leak memory in this code, and releasing the array yourself will cause a crash when the array is autoreleased at the end of the run loop.
Most Cocoa classes provide a couple of ways of making a new object, and are very consistent with this convention:
[[NSSomeObject alloc] init] : you are responsible for releasing the object (instance method).
[NSSomeObject someObject] : the object will be autoreleased for you, usually at the end of the run loop (class method). It's roughly equivalent to [[[NSSomeObject alloc] init] autorelease].
The proper use of the instance method would be:
a = [[NSMutableArray alloc] init];
// do stuff
[a release];
The proper use of the class method would be:
a = [NSMutableArray array];
// do stuff, array is in the autorelease pool
Note that Apple has recommended you stay away from the convenience methods as much as possible to improve performance. This is controversial advice, may not save much processor time, and separates the alloc-init from the release on an object you may not actually care much about keeping.
From the Cocoa Memory Managment Rules:
You take ownership of an object if you create it using a method whose name begins with “alloc” or “new” or contains “copy” (for example, alloc, newObject, or mutableCopy), or if you send it a retain message. You are responsible for relinquishing ownership of objects you own using release or autorelease. Any other time you receive an object, you must not release it.
Therefore with the line:
a = [NSMutableArray array];
you do not take ownership of the array, and it will be passed to you autoreleased. The memory will be handled for you automatically by the autorelease pool, and once it is no longer being used, it will be released for you. If you want to keep the array outside the current event, however, you must retain it, otherwise it will be released for you.
Yes, if you want it to stick around.
The returned object is an autoreleased object which will get deallocated when its autorelease pool gets purged.
All the array class methods that begin with "array" return these types of autoreleased objects.
Read this doc by Apple.
That's valid. It may help to just manage things manually when you have questions, to learn.
There is a convention:
init prefixes (init, initWithString:) indicate a retain count of 1, where
objectname prefixes (string, stringWithString:) indicates an autoreleased object
I have had the habit, for years, to release what I can at the call site, rather than pushing it to be autoreleased. Some autorelease issues then become painfully difficult to track down. Sure, autorelease is a convenience to the programmer in this case (provided nothing goes wrong), but adversely affects reuse, clarity, and performance (moreso in large codebases/programs).