Search code examples
iphoneuiimagecore-graphicsquartz-2dcore-foundation

What's the right memory management pattern for buffer->CGImageRef->UIImage?


I have a function that takes some bitmap data and returns a UIImage * from it. It looks something like so:

UIImage * makeAnImage() 
{
    unsigned char * pixels = malloc(...);
    // ...
    CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, pixels, pixelBufferSize, NULL);
    CGImageRef imageRef = CGImageCreate(..., provider, ...);
    UIImage * image =  [[UIImage alloc] initWithCGImage:imageRef];
    return [image autorelease];
}

Can anyone explain exactly who owns what memory here? I want to clean up properly, but I'm not sure how to do it safely. Docs are fuzzy on these. If I free pixels at the end of this function after creating the UIImage, and then use the UIImage, I crash. If I Release the provider or the imageRef after creating the UIImage, I don't see a crash, but they're apparently passing the pixels all the way through, so I'm skittish about releasing these intermediate states.

(I know per CF docs that I should need to call release on both of the latter because they come from Create functions, but can I do that before the UIImage is used?) Presumably I can use the provider's dealloc callback to cleanup the pixels buffer, but what else?

Thanks!


Solution

  • The thumb rule here is "-release* it if you don't need it".

    Because you no longer need provider and imageRef afterwards, you should -release all of them, i.e.

    UIImage * image =  [[UIImage alloc] initWithCGImage:imageRef];
    CGDataProviderRelease(provider);
    CGImageRelease(imageRef);
    return [image autorelease];
    

    pixel is not managed by ref-counting, so you need to tell the CG API to free them for you when necessary. Do this:

    void releasePixels(void *info, const void *data, size_t size) {
       free((void*)data);
    }
    ....
    
    CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, pixels, pixelBufferSize, releasePixels);
    

    By the way, you can use +imageWithCGImage: instead of [[[* alloc] initWithCGImage:] autorelease]. Even better, there is +imageWithData: so you don't need to mess with the CG and malloc stuff.

    (*: Except when the retainCount is already supposedly zero from the beginning.)