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.
Related
I have the following code in a loop iterating over the different document objects:
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
NSData* data = [document primitiveValueForKey:#"data"];
[document.managedObjectContext refreshObject:document mergeChanges:NO];
[pool release];
The "data" property is a large blob (a 1MB image).
And as I monitor the memory with the Allocation Instrument memory usage is increasing. I cannot find where the leak is coming from and how to remove it.
Thanks!
Something is wrong with your sample code, did you mean:
NSData *data = [document primitiveValueForKey:#"data"];
As data is currently not assigned within the scope of your autoreleasepool it is also not released with with your autoreleasepool
Why are you using primitiveValueForKey and not a dynamic accessor?
The dynamic accessors are much more
efficient, and allow for compile-time
checking.
How about calling [pool drain] instead of [pool release]?
I managed to solve the problem by doing : [document.managedObjectContext processPendingChanges] right before draining the pool. However, I don't understand what pending changes would be there? Could someone enlighten me on that?
Your observation that processPendingChanges seems to solve the problem suggests to me that, as you import, the UndoManager for your NSManagedObjectContext is keeping track of all the changes you make as you do your bulk import.
What processPendingChanges is doing (as I understand it) is pushing changes stored in the managedObjectContext to the persistent store.
Try [[document managedObjectContext] setUndoManager:nil] (or create a new managedObjectContext for the import and set its undoManager to nil, if your document.managedObjectContext is the 'main' managedObjectContext and you don't want to turn off undo registration.
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.
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.
In the following code example from the Core Data Programming Guide, NSFetchRequest is created
with autorelease while NSSortDescriptor is not created with autorelease. Why wasn't NSSortDescriptor created with autorelease? Is it a matter of preference?
NSManagedObjectContext *moc = [self managedObjectContext];
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:#"Employee"
inManagedObjectContext:moc];
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:entityDescription];
// Set example predicate and sort orderings...
NSNumber *minimumSalary = ...;
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(lastName LIKE[c]'Worsley') AND (salary > %#)", minimumSalary];
[request setPredicate:predicate];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"firstName"
ascending:YES];
[request setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];
[sortDescriptor release];
NSError *error;
NSArray *array = [moc executeFetchRequest:request error:&error];
if (array == nil){
// Deal with error...
}
When you autorelease, you're basically saying: "I don't need this any longer, but anyone else is free to pick it up (before the auto release pool is drained)". When you explicitly relase an object you're saying: "I don't need this any longer and unless anyone else has already said otherwise (acquired), it should be deallocated immediately."
Consequently, autorelease is not normally the wrong thing to. It is required when you want to pass objects back to the sender of a message without requiring the sender to take care of releasing the object.
To Autorelease or Not to Autorelease
That is the question.
Whether tis nobler for the coder to suffer the slings and arrows of memory leaks or take arms against a sea of deferenced pointers and by retaining them, end them... Tis a consummation devoutly to be wished! Aye, there's the rub! ... For in those over released objects, what crashes may come when we reference objects that are not there must give us pause.
I couldn't help myself. I'll take the rep hit. I regret nothing!
The request object is going to be returned to the caller, whereas the sortDescriptor is being used and then discarded.
The rationale for autorelease is simple. Without it, any object returned from a function would need to be released by the caller if they didn't need it. Using autorelease means that functions may return an object which if the caller doesn't care about, or if they are going to look at it but not keep a reference to it, then they can just use it without adding additional code to free it. Only if they're keeping a reference do they need to retain it.
It's worth thinking about exactly what autorelease means. When you call autorelease on an object, it adds the object to a list, and when your application loop finishes, it'll have release called on it. This makes autorelease exactly equivalent to a delayed release.
The apple document on memory management is excellent and worth careful reading. http://developer.apple.com/iPhone/library/documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html
The question is this: how does the use of autorelease or release affect the lifetime of an object?
In both of your examples it makes no difference.
Both NSFetchRequest and NSSortDescriptors will be alive until the end of the method, regardless of whether they're released or autoreleased.
If you create an instance of an object and then give it to another object (say, an NSArray), it will remain alive regardless of whether you call release or autorelease.
Both retain and autorelease, functionally retain an object but they don't merge. The differences is that retain counts can only be decremented by another object whereas autoreleased counts are decremented automatically when the NSAutoreleasePool is drained. If no other object has retained the autoreleased object by the time the pool drains, it goes poof.
Basically, you use autorelease when you want to make sure an object hangs around in the current method and can be passed to other objects but you don't want to have to track its release yourself.
In your example code, autorelease is just a safety measure. The NSPredicate object is released because its job is finished but the coder wanted to make sure that the NSFetchRequest object hung around. You don't have to use "autorelease" in this instance but if you lost count of your releases the fetchRequest might vanish. On the other hand, you don't want it orphaned and leaking so you use autorelease to clean up when the pool the object is in drains.
The most common use of autorelease is when you are creating a variable number of objects each time. You don't want to have to track them all so you autorelease them and let the pool take care of them. (Even better, you create a local pool and drain it as soon as your done.)
In the Apple API standard, any method that creates a new object without the key words 'init','new' or 'create' returns an autoreleased object.
-[NSString initWithString:] is not autorelease but -[NSString stringWithString:] is. This causes problems in non-Garbage collection environments because stringWithString: returns a string that appears to behave like a retained object but later it will suddenly vanish seemingly at random when the autorelease pool it was created in drains.
Edit01: From the Apple Docs
An autorelease pool is an instance of
NSAutoreleasePool that “contains”
other objects that have received an
autorelease message; when the
autorelease pool is deallocated it
sends a release message to each of
those objects. An object can be put
into an autorelease pool several
times, and receives a release message
for each time it was put into the
pool. Thus, sending autorelease
instead of release to an object
extends the lifetime of that object at
least until the pool itself is
released (the object may survive
longer if it is retained in the
interim).
The retain counts and autorelease both keep an object alive by the same basic (but separate) count mechanism. The major difference is which owning object sends the release. With retained counts, its another object but for an autoreleased count its the autorelease pool.
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.