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

后端 未结 4 1865
Happy的楠姐
Happy的楠姐 2021-02-07 14:11

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          


        
相关标签:
4条回答
  • 2021-02-07 14:39
    unsigned char * pixels = malloc(...);
    

    You own the pixels buffer because you mallocked it.

    CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, pixels, pixelBufferSize, NULL);
    

    Core Graphics follows the Core Foundation rules. You own the data provider because you Created it.

    You didn't provide a release callback, so you still own the pixels buffer. If you had provided a release callback, the CGDataProvider object would take ownership of the buffer here. (Generally a good idea.)

    CGImageRef imageRef = CGImageCreate(..., provider, ...);
    

    You own the CGImage object because you Created it.

    UIImage * image =  [[UIImage alloc] initWithCGImage:imageRef];
    

    You own the UIImage object because you allocked it.

    You also still own the CGImage object. If the UIImage object wants to own the CGImage object, it will either retain it or make its own copy.

    return [image autorelease];
    

    You give up your ownership of the image.

    So your code leaks the pixels (you didn't transfer ownership to the data provider and you didn't release them yourself), the data provider (you didn't release it), and the CGImage (you didn't release it). A fixed version would transfer ownership of the pixels to the data provider, and would release both the data provider and the CGImage by the time the UIImage is ready. Or, just use imageWithData:, as KennyTM suggested.

    0 讨论(0)
  • 2021-02-07 14:45
    unsigned char * pixels = malloc(...);
    

    I also had problem with malloc/free after using CGImageCreate I finally found good and simple solution. I just replace line:

    CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, pixels, pixelBufferSize, NULL);
    

    with:

    NSData *data = [NSData dataWithBytes:pixels length:pixelBufferSize];
    CGDataProviderRef provider = CGDataProviderCreateWithCFData((CFDataRef)data);
    

    Just after that i could free mallocked memory:

    free (pixels);
    
    0 讨论(0)
  • 2021-02-07 14:48

    Aye, this code makes me queasy. As an old rule-to-live by, I try not to mix and match C and C++, and C/Objective-C in the same function/method/selector.

    How about breaking this up into two methods. Have change this makeAnImage into makeAnImageRef and pull up the UIImage creation into another Obj-C selector.

    0 讨论(0)
  • 2021-02-07 14:58

    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.)

    0 讨论(0)
提交回复
热议问题