Toll-free bridging

Assume this code is compiled under ARC:

BOOL method(unsigned a)
{
    NSString *string = [[NSString alloc] initWithCString:"hi"];
    if (a & 1 == 1)
    {
         CFStringRef CF = (CFStringRef)CFBridgingRetain(string);
         printf("%s", CFStringGetCStringPtr(CF, kCFStringEncodingUTF8));
         CFRelease(CF);
    }
}

Will string get over-released in this example? What if I did the reverse?

BOOL method(unsigned a)
{
    CFStringRef string = CFStringCreateWithCString(NULL, "Hello World!", kCFStringEncodingUTF8);
    if (a & 1 == 1)
    {
         NSString *ns = (NSString *)CFBridgingRelease(string);
         printf("%s", CFStringGetCStringPtr(CF, kCFStringEncodingUTF8);
    }
   if (string)
       CFRelease(string);
}

Accepted Reply

Your first snippet is correct. Your second one is wrong.

To fix the second snippet, replace this:

NSString *ns = (NSString *)CFBridgingRelease(string);

with this:

NSString *ns = (__bridge NSString *) string;

That’s because ns isn’t taking ownership of the string, it’s just using it temporarily.

Alternatively, and this is what I try to do almost always, write code like this:

CFStringRef string = CFStringCreateWithCString(…);
NSString * ns = CFBridgingRelease(string);
if (a & 1 == 1)
{
    … work with `ns`
}
// No `CFRelease`!

That is, get out of ‘CF space’ as quickly as possible and let ARC do all the heavy lifting.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Replies

Your first snippet is correct. Your second one is wrong.

To fix the second snippet, replace this:

NSString *ns = (NSString *)CFBridgingRelease(string);

with this:

NSString *ns = (__bridge NSString *) string;

That’s because ns isn’t taking ownership of the string, it’s just using it temporarily.

Alternatively, and this is what I try to do almost always, write code like this:

CFStringRef string = CFStringCreateWithCString(…);
NSString * ns = CFBridgingRelease(string);
if (a & 1 == 1)
{
    … work with `ns`
}
// No `CFRelease`!

That is, get out of ‘CF space’ as quickly as possible and let ARC do all the heavy lifting.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

I don't think the second snippet would be fixed by writing

NSString *ns = (__bridge_transfer NSString *)CFBridgingRelease(string);

because as far as I know, __bridge_transfer is basically the same as CFBridgingRelease. The problem with the second snippet is that when the local variable ns goes out of scope, the string will be released, but the local variable string is not set to nil, hence CFRelease will be passed a bad pointer.

You are correct. I meant to say simply __bridge. And I should’ve also deleted the CFBridgingRelease. I’ve gone back and fixed my post. Thanks for noticing!

[Clearly it was way too early on Monday morning to be answering CF memory management problems )-: ]

This time I put the code into the compiler so that it checked my reasoning. One thing to note here is that the static analyser is good about finding these problems. It flagged the problem I was talking about and was happy with my new fix.

Having said that, the compiler also gets grumpy about this construct:

if (a & 1 == 1)

By sheer coincidence this probably does what it looks like it does but it really should be this:

if ((a & 1) == 1)

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"