iosobjective-cautomatic-ref-countingcgimagereftoll-free-bridging

Correct way to handle CGImageRef in containers under ARC


I am new to incorporating arc in my projects. I am trying to understand __bridge and its little friends so that I can properly cast my CGImageRef's when adding and removing them from containers.

I am getting a "Potential leak of an object stored…" on one of my lines. Here is the basic cycle of my code:

CGImageRef renderedRef = CGBitmapContextCreateImage(UIGraphicsGetCurrentContext());
[_array addObject: (__bridge_transfer id)renderedRef];//_array is an iVar

then somewhere down the road I do this:

    CGImageRef iRef = (__bridge_retained CGImageRef)array[0];
//then I do something fancy with iRef
//at the end of the method, I get "Potential leak of an object stored…"
//I have no idea what to do
//I've tried CGImageRelease(iRef); but it still doesn't change the warning. 

Can someone shed some light on this? Also, I have tried just using __bridge but that doesn't make a difference.

Edit 1:

I expanded the analyzer results and followed what was happening. It was because I was using iRef in a method like so: [self doSomethingFancy:iRef]; and in that method, iRef was being retained but not released. So that fixes the warning, but I'm still a bit puzzled.

I am not quite clear on when to use the various __bridge casts. Under ARC, does the following increase the reference count?

CGImageRef iRef = (__bridge CGImageRef)array[0];

Also, at some point if I tell my _array iVar to removeAllObjects, will that decrement their reference counts properly?


Solution

  • // This WILL NOT increment the image's retain count.
    CGImageRef iRef = (__bridge CGImageRef)array[0];
    
    // This WILL increment the image's retain count.
    CGImageRef iRef = (__bridge_retained CGImageRef)array[0];
    

    Since __bridge_retained increments the retain count, you need to decrement the retain count at some point later. Because __bridge_retained acts like CFRetain, Apple created a __bridge_retained wrapper called CFBridgingRetain that reminds you of this:

    // This WILL increment the image's retain count.
    CGImageRef iRef = CFBridgingRetain(array[0]);
    

    Whenever you see CFRetain or CFBridgingRetain, you know you need to release the object at some point later.

    Similarly, you can use CFBridgingRelease instead of __bridge_transfer to decrement the retain count of a CF object. Example:

    [_array addObject:CFBridgingRelease(renderedRef)];
    

    You can use CFBridgingRelease to balance a CFRetain or a CFBridgingRetain. It returns an id that ARC manages.

    A normal NSMutableArray retains each of its elements. You can tell it to become empty:

    [_array removeAllObjects];
    

    When you do this, it will release each of its elements, balancing the retain it performed on each element.

    Thus there is no memory leak in this code:

    CGImageRef image = CGImageCreate(...);
    [_array addObject:CFBridgingRelease(image)];
    [_array removeAllObjects];
    

    or in this code:

    CGImageRef image = CGImageCreate(...);
    [_array addObject:CFBridgingRelease(image)];
    
    CGImageRef image2 = CFBridgingRetain([_array lastObject]);
    [_array removeLastObject];
    CGContextDrawImage(gc, rect, image2);
    CGImageRelease(image2);