views:

988

answers:

2

Like in this post:

I'm having a similar problem. The pointer from the malloc in create_bitmap_data_provider is never freed. I've verified that the associated image object is eventually released, just not the provider's allocation. Should I explicitly create a data provider and somehow manage it's memory? Seems like a hack.

CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(NULL, blah blah blah);
CGColorSpaceRelease(colorSpace);

// ... draw into context

CGImageRef imageRef = CGBitmapContextCreateImage(context);
UIImage * image = [[UIImage alloc] initWithCGImage:imageRef];

CGImageRelease(imageRef);
CGContextRelease(context);


After fbrereto's answer below, I changed the code to this:

- (UIImage *)modifiedImage {
    CGSize size = CGSizeMake(width, height);

    UIGraphicsBeginImageContext(size);
    CGContextRef context = UIGraphicsGetCurrentContext();

    // draw into context   

    UIImage * image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return image;  // image retainCount = 1
}    

// caller: 
{
    UIImage * image = [self modifiedImage]; 
    _imageView.image = image; // image retainCount = 2
}

// after caller done, image retainCount = 1, autoreleased object lost its scope

Unfortunately, this still exhibits the same issue with a side effect of flipping the image horizontally. It appears to do the same thing with CGBitmapContextCreateImage internally.

I have verified my object's dealloc is called. The retainCount on the _imageView.image and the _imageView are both 1 before I release the _imageView. This really doesn't make sense. Others seem to have this issue as well, I'm the last one to suspect the SDK, but could there be an iPhone SDK bug here???

A: 

Instead of manually creating your CGContextRef I'd suggest you leverage UIGraphicsBeginImageContext as demonstrated in this post. More details on that set of routines can be found here. I trust it'll help to resolve this issue, or at the very least give you less memory you have to manage yourself.

UPDATE:

Given the new code, the retainCount of the UIImage as it comes out of the function will be 1, and assigning it to the imageView's image will cause it to bump to 2. At that point deallocating the imageView will leave the retainCount of the UIImage to be 1, resulting in a leak. It is important, then, after assigning the UIImage to the imageView, to release it. It may look a bit strange, but it will cause the retainCount to be properly set to 1.

fbrereto
Added to the question after this answer. Didn't help.
Shawn
Added to my answer after you added to your question. Hopefully it'll help.
fbrereto
It has to be my code, I can't blame the SDK (3.0) yet. But the retainCount from the function will be an _autoreleased_ 1. As soon as the scope of the caller is gone, that will decrement by 1. So after the set on the imageView, it will be 2, but as soon as the caller's scope is done, it goes to one. In the dealloc, I've verified in the release of the object both the imageView and the imageView.image both have a retainCount of 1.
Shawn
If the object is autoreleased then you have to make sure to drain the pool in order to deallocate the image. Are you doing that?
fbrereto
The application eventually runs out of memory because of this. I shouldn't have to drain the pool for all my autoreleased objects. It should eventually free itself like all the other autoreleased objects I have. This little beast doesn't want to relinquish itself for some reason. I could understand if the thought behind draining it if you wanted to check the memory immediately, but I run this code over and over and the memory keeps growing and growing.
Shawn
In that case I'd create an autorelease pool for the scope where you're running this code repeatedly and drain it. The autorelease pool is not like a garbage collector IIUC- it won't drain automatically when memory starts to pile up. Even if it does, you're better off controlling the memory yourself with a second pool that you explicitly drain.
fbrereto
It's not in a tight loop. The logic occurs on load of the view, so when I pop the view, I expect the UIKit's autorelease pool to do it's job. This is from the SDK: 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. My code doesn't fall into the exceptions. Something else is going on here...
Shawn
This answer is incorrect after UPDATE:. Reference count is fine. It's not required to drain the pool.
Shawn
A: 

You're not the only one with this problem. I've had major problems with CGBitmapContextCreateImage(). When you turn on Zombie mode, it even warns you that memory is released twice (when it's not the case). There's definitely a problem when mixing CG* stuff with UI* stuff. I'm still trying to figure out how to code around this issue.

Side note: calling UIGraphicsBeginImageContext is not thread-safe. Be careful.

Philippe Leybaert
Well, if you figure it out, please post an answer! :)
Shawn
Yes, UIGraphicsBeginImageContext isn't threadsafe, but that's not all.
Shawn