views:

87

answers:

4

I have an class method which generates a UIImage, like this:

+ (UIImage*)imageWithFileName:(NSString*)imgFile {
    UIImage *img = nil;

    NSBundle *appBundle = [NSBundle mainBundle];
    NSString *resourcePath = [appBundle pathForResource:imgFile ofType:nil];

    if (resourcePath != nil) {
        NSURL *imageURL = [NSURL fileURLWithPath:resourcePath];
        NSData *data = [[NSData alloc] initWithContentsOfURL:imageURL];

        img = [UIImage imageWithData:data]; // should be autoreleased!!

        [data release];
    }

    return img;
}

However, when I use this, the image data is NEVER freed. There is definitely a memory bug with this, although I didn't break any memory management rule I am aware of. My guess is that because this is a class method which gets called from instance methods, There is no active autorelease pool in place or it's one that only gets drained when I quit the app. Could that be right?

+2  A: 

UIImage retains the image data, so if you are actually leaking memory you should check if you are freeing the returned img.

The data variable retain count is this:

  • allocation: 1
  • create an image with data: 2
  • [data release]: 1

so until the img is not freed, the provided data it is not released (that makes sense, since the image needs the data)

you can use retaincount to check for the current retain count of an NSObject derived item

garph0
Don't look at the retainCounts! They're generally misleading. There's no telling what UIImage does with the data passed to it. I also try not to change my perspective when thinking about object ownership - you should only care about what **your** class owns. Once you pass objects elsewhere, you cannot assume that you know what they do with it.
Matt B.
Note that I am calling [UIImage imageWithData:data]. This method returns an autoreleased object. So releasing that breaks against the memory management rules. An ARP is responsible. So the question still is: Is there any ARP, and if yes, which one and when does it get drained?
dontWatchMyProfile
Yes, the UIImage it's autoreleased, so you should not care about it.As Matt B. told in its answer, from your point of view in this piece of code the memory management it's ok.What happens to UIImage depends entirely on what you do with it when you exit the function: do you assign it to a superview? That, would increase the retain count, for example.
garph0
Believe me, I don't do anything wrong in my memory management. The exact same method converted into an instance method returning an +1RC obj with "new" prefix and autoreleasing it in the caller works very well. There's definitely no tight ARP around.
dontWatchMyProfile
+3  A: 

Once you've passed the data on to img, it's out of your hands. It's possible that UIImage keeps the raw data around in its internal implementation. But it doesn't matter. From your perspective, you've appropriately released the data, and it is entirely up to img to determine whether it wishes to keep it around.

Do you ever explicitly retain the returned UIImage? Or pass it to another class? If, for example, you place it into a UIView, the view will retain img until it is no longer needed.

With regards to your explicit question: there is a main run-loop AutoreleasePool. It generally gets drained after every event loop.

Matt B.
No, of course I don't release that UIImage explicitly because it should be autoreleased. Releasing it manually would cause an overrelease when the ARP gets drained. However, it seems that either there is no ARP at all, or there is an ARP at such an low level that it only gets drained when the app quits.
dontWatchMyProfile
@mystify: Yeah, that was a typo. See my edit.
Matt B.
I see. Even if there is a ARP around, it seems to not autorelease the object if it was created in an class method. tested it with an instance method. exact same code, just instance method. there it works. class method = nope.
dontWatchMyProfile
A: 

After some tests I have figured out, that the problem is indeed related to Autorelease Pools. If I use the exact same code within an instance method, there is absolutely no problem.

But as soon as I use that in a class method, the autoreleased UIImage object never gets released. Whatever autorelease pool of the autorelease pool stack is the topmost in class methods, it's seriously not the same as in instance methods. I am pretty sure now that class methods receive a very low level autorelease pool which only gets drained or released when the app quits.

So be careful doing autoreleased stuff in class methods. Of course, it doesn't help at all to create an ARP locally, because what you might need (like I do in this case) is to return an autoreleased object.

I will change my code to +newImageWithFileName: and return an not-autoreleased object, so the receiver definitely has to release it or may send -autorelease to that.

I hope someone can provide some more details on this issue.

dontWatchMyProfile
I have a hard time believing this is the correct solution. Class methods are nothing more than instance methods for class objects. Internally, class methods are identical to instance methods. In addition, any autorelease pool that's in scope of an executing method will also be in scope for any methods that method invokes. Just because you're using a `+` instead of a `-` shouldn't affect the rules of memory management at all. My guess is that you've taken some Advil for a broken arm, but haven't figured out what the real issue is.
Dave DeLong
have you tried it? I tried, twice. It seems to be true.
dontWatchMyProfile
Dave's right. It doesn't matter where the method resides, all that matters is where and how it is called. Something else is going on in this case. Unless you manually create one yourself, there is only one autorelease pool for your application.
Brad Larson
@Brad technically there's only one ARP, but that pool is created and destroyed with each iteration of the runloop.
Dave DeLong
@Dave DeLong - Sorry, I got a little confused by the one that gets created in main.m. The Memory Management Programming Guide backs you up: "The Application Kit automatically creates a pool at the beginning of an event cycle (or event-loop iteration), such as a mouse down event, and releases it at the end, so your code normally does not have to worry about them."
Brad Larson
Well guys, in theory a lot of things are working. Please try it. I did twice now, with two projects. The exact same thing once as class method, once as instance method. Class method = no autorelease. Instance method = autoreleased.
dontWatchMyProfile
@mystify - Can you create a simple test project that illustrates this? I really find this hard to believe.
Brad Larson
+1  A: 

The real question I think is, how are you measuring that the memory is not released.

Autorelease pools are all related to the thread and runloop you are in - because they free memory when a call returns all the way to the main runloop. It doesn't matter if you are calling a class method or an instance method or even a C function, autorelease will work the same in all cases.

I know in your testing you found there are differences, but simply put if you see a difference it is for some other reason - because autorelease always works the same across the system as far as freeing memory if you are in the same runloop.

Kendall Helmstetter Gelner
Two things: In a loop I loaded hundreds of big images, using the class method. App crashes after a while, because they never get freed. Object Alloc instrument shows exploding memory footprint over time. Then simply made an instance method out of it. Result: Works fine. Then another test: Class method, but no autoreleased object. Instead I only create that UIImage without convenience constructor and return the obj with no release or autorelease. Method name with new prefix. Caller autoreleases the received object. Result: Works.
dontWatchMyProfile
Loading images how though? imageNamed? from data blocks? Using the file path?I appreciate you trying to experiment but you can download source to the OBJC runtime and simply put, there is zero difference using autorelease in a class method vs. an instance method. It's not even aware of which was the case when you used Autorelease, nor can it be. I agree you are seeing an issue but you have an incorrect diagnosis.
Kendall Helmstetter Gelner