Beginning ARC in iOS 5 Tutorial Part 2

Note from Ray: This is the twelfth iOS 5 tutorial in the iOS 5 Feast! This tutorial is a free preview chapter from our new book iOS 5 By Tutorials. Matthijs Hollemans wrote this chapter – the same guy who wrote the iOS Apprentice Series. Enjoy! This is a post by iOS Tutorial Team member […] By Ray Wenderlich.

Leave a rating/review
Save for later
Share
You are currently viewing page 3 of 5 of this article. Click here to view the first page.

Toll-Free Bridging

Let’s fix that one last method so we can run the app again.

descr

This method uses the CFURLCreateStringByAddingPercentEscapes() function to URL-encode a string. We use it to make sure any spaces or other characters in the search text that the user types get converted to something that is valid for use in an HTTP GET request.

The compiler gives several errors:

  • Cast of C pointer type ‘CFStringRef’ to Objective-C pointer type ‘NSString *’ requires a bridged cast
  • Cast of Objective-C pointer type ‘NSString *’ to C pointer type ‘CFStringRef’ requires a bridged cast
  • Semantic Issue: ‘autorelease’ is unavailable: not available in automatic reference counting mode
  • Automatic Reference Counting Issue: ARC forbids explicit message send of
    ‘autorelease’

These last two errors are really the same and simply mean that we cannot do [autorelease]. Let’s get rid of that first. What remains is this:

- (NSString *)escape:(NSString *)text
{
  return (NSString *)CFURLCreateStringByAddingPercentEscapes(
    NULL,
    (CFStringRef)text,
    NULL,
    (CFStringRef)@"!*'();:@&=+$,/?%#[]",
    CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding));
}

The other two errors have to do with casts that apparently should be “bridged”. There are three casts in this method:

  • (NSString *)CFURLCreateStringByAddingPercentEscapes(…)
  • (CFStringRef)text
  • (CFStringRef)@”!*'();:@&=+$,/?%#[]”

The compiler only complains about the first two. The third one is a cast of a constant object and that doesn’t require any special memory management. This is a string literal that will be baked into the application executable. Unlike “real” objects, it is never allocated or freed.

If you wanted to, you could also write this as:

CFSTR("!*'();:@&=+$,/?%#[]")

The CFSTR() macro creates a CFStringRef object from the specified string. The string literal is a regular C string now and therefore doesn’t begin with the @ sign. Instead of making an NSString object and casting it to a CFStringRef, we directly make a CFStringRef object. Which one you like better is largely a matter of taste, as they both deliver the exact same results.

Bridged casts are necessary when you move an object between worlds. On the one hand there is the world of Objective-C, on the other there is Core Foundation.

For most apps these days there isn’t a big need to use Core Foundation, you can do almost anything you want from comfortable Objective-C classes. However, some lower-levels APIs such as Core Graphics and Core Text are based on Core Foundation and it’s unlikely there will ever be an Objective-C version of them. Fortunately, the designers of iOS made it really easy to move certain objects between these two different kingdoms. And you won’t be charged at thing!

For all intents and purposes, NSString and CFStringRef can be treated as the being same. You can take an NSString object and use it as if it were a CFStringRef, and take a CFStringRef object and use it as if it were an NSString. That’s the idea behind toll-free bridging. Previously that was as simple as doing a cast:

CFStringRef s1 = [[NSString alloc] initWithFormat:@"Hello, %@!", 
  name];

Of course, you also had to remember to release the object when you were done with it:

CFRelease(s1);

The other way around, from Core Foundation to Objective-C, was just as easy:

CFStringRef s2 = CFStringCreateWithCString(kCFAllocatorDefault,
  bytes, kCFStringEncodingMacRoman);
NSString *s3 = (NSString *)s2;

// release the object when you're done
[s3 release];

Now that we have ARC, the compiler needs to know who is responsible for releasing such casted objects. If you treat an NSObject as a Core Foundation object, then it is no longer ARC’s responsibility to release it. But you do need to tell ARC about your intentions, the compiler cannot infer this by itself. Likewise, if you create a Core Foundation object but then cast it to an NSObject, you need to tell ARC to take ownership of it and delete that object when its time comes. That’s what the bridging casts are for.

Let’s look at the simple case first. The CFURLCreateStringByAddingPercentEscapes() function takes a bunch of parameters, two of which are CFStringRef objects. These are the Core Foundation equivalent of NSString. Previously we could just cast these NSString objects into a CFStringRef but with ARC the compiler needs more information. We’ve already dealt with the constant string, which requires no bridging cast because it is a special object that will never be released. However, the text parameter is another story.

The text variable is an NSString object that was given to this method as a parameter. Like local variables, method parameters are strong pointers; their objects are retained upon entry to the method. The value from the text variable will continue to exist until the pointer is destroyed. Because it is a local variable, that happens when the escape: method ends.

We want ARC to stay the owner of this variable but we also want to temporarily treat it as a CFStringRef. For this type of situation, the __bridge specifier is used. It tells ARC that no change in ownership is taking place and that it should release the object using the normal rules.

We’ve already used __bridge before in SoundEffect.m:

OSStatus error = AudioServicesCreateSystemSoundID((__bridge 
  CFURLRef)fileURL, &theSoundID);

The exact same situation applies there. The fileURL variable contains an NSURL object and is managed by ARC. The AudioServicesCreateSystemSoundID() function, however, expects a CFURLRef object. Fortunately, NSURL and CFURLRef are toll-free bridged so we can cast the one into the other. Because we still want ARC to release the object when we’re done with it, we use the __bridge keyword to indicate that ARC remains in charge.

Change the escape: method to the following:

- (NSString *)escape:(NSString *)text
{
  return (NSString *)CFURLCreateStringByAddingPercentEscapes(
    NULL,
    (__bridge CFStringRef)text,
    NULL,
    CFSTR("!*'();:@&=+$,/?%#[]"),
    CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding));
}

That takes care of all but one of the errors.

Most of the time when you cast an Objective-C object to a Core Foundation object or vice versa, you’ll want to use __bridge. However, there are times when you do want to give ARC ownership or relieve ARC of its ownership. In that case there are two other bridging casts that you can use:

  • __bridge_transfer: Give ARC ownership
  • __bridge_retained: Relieve ARC of its ownership

There is one error remaining in our source file, on the line:

return (NSString *)CFURLCreateStringByAddingPercentEscapes(

If you click on the error message, the following Fix-it will pop up:

descr

It gives two possible solutions: __bridge and __bridge_transfer. The correct choice here is __bridge_transfer. The CFURLCreateStringByAddingPercentEscapes() function creates a new CFStringRef object. Of course, we’d rather use NSString so we need to do a cast. What we’re really attempting to do is this:

CFStringRef result = CFURLCreateStringByAddingPercentEscapes(. . .);
NSString *s = (NSString *)result;
return s;

Because the function has the word “Create” in its name, it returns a retained object. Someone is responsible for releasing that retained object at some point. If we weren’t returning this object as an NSString, then our code may have looked something like this:

- (void)someMethod
{
	CFStringRef result = CFURLCreateStringByAddingPercentEscapes
      (. . .);
    
	// do something with the string
	// . . .
    
	CFRelease(result);
}

Remember that ARC only works for Objective-C objects, not for objects that are created by Core Foundation. You still need to call CFRelease() on such objects yourself!

What we want to do in escape is convert that new CFStringRef object to an NSString object, and then ARC should automatically release that string whenever we’re no longer using it. But ARC needs to be told about this. Therefore, we use the __bridge_transfer modifier to say: “Hey ARC, this CFStringRef object is now an NSString object and we want you to dispose of it, so that we don’t have to call CFRelease() on it ourselves.”

The method now becomes:

- (NSString *)escape:(NSString *)text
{
  return (__bridge_transfer NSString *)
   CFURLCreateStringByAddingPercentEscapes(
    NULL,
    (__bridge CFStringRef)text,
    NULL,
    CFSTR("!*'();:@&=+$,/?%#[]"),
    CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding));
}

We take the result from CFURLCreateStringByAddingPercentEscapes(), which is a CFStringRef object, and convert it into an NSString that is now under the care of ARC.

If we were to just use __bridge instead, then your app would have a memory leak. ARC doesn’t know that it should release the object when you’re done with it and no one calls CFRelease(). As a result, the object will stay in memory forever. It’s important that you pick the proper bridge specifier!

To make it a little easier to remember which type of bridge to use, there is a helper function named CFBridgingRelease(). It does the exact same thing as a __bridge_transfer cast but the meaning is clearer. The final version of the escape: method becomes:

- (NSString *)escape:(NSString *)text
{
  return CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(
   NULL,
   (__bridge CFStringRef)text,
   NULL,
   CFSTR("!*'();:@&=+$,/?%#[]"),
   CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding)));
}

Instead of doing (__bridge_transfer NSString *) we now wrap CFURLCreateStringByAddingPercentEscapes() inside a call to CFBridgingRelease(). This CFBridgingRelease() function is defined as an inline function so it is no slower than doing the cast directly. It is named CFBridgingRelease() because you use it anywhere you would otherwise do a CFRelease() to balance the creation of the new object.

Another common framework that requires these bridging casts is the AddressBook framework. For example:

- (NSString *)firstName
{
	return CFBridgingRelease(ABRecordCopyCompositeName(...));
}

Anywhere you call a Core Foundation function named Create, Copy, or Retain you must do CFBridgingRelease() to safely transfer the value to ARC.

What about the other one, __bridge_retained? You would use that going the other way around. Suppose you have an NSString and you need to give that to some Core Foundation API that wants to take ownership of your string object. You don’t want ARC to also release that object, because then it would be released one time too many and apps have a tendency to crash when that happens. In other words, you use __bridge_retained to give the object to Core Foundation so that ARC is no longer responsible for releasing it. An example:

NSString *s1 = [[NSString alloc] initWithFormat:@"Hello, %@!", name];

CFStringRef s2 = (__bridge_retained CFStringRef)s1;

// do something with s2
// . . .

CFRelease(s2);

As soon as the (__bridge_retained CFStringRef) cast happens, ARC considers itself no longer duty-bound to release the string object. If you had used __bridge in this example, then the app would likely crash. ARC might deallocate the string object before the Core Foundation code is done with it.

There is also a helper function for this kind of cast: CFBridgingRetain(). You can tell by the name that it makes Core Foundation do a retain on the object. The above example is better written as:

CFStringRef s2 = CFBridgingRetain(s1);

// . . .

CFRelease(s2);

Now the meaning of the code is clearer. The call to CFRelease() is properly balanced by CFBridgingRetain(). I doubt you’ll need to use this particular bridge type often. Off the top of my head, I can’t think of a single API that is commonly used that requires this.

It is unlikely that you’ll have a lot of Core Foundation code in your apps. Most frameworks that you’ll use are Objective-C, with the exception of Core Graphics (which doesn’t have any toll-free bridged types), the Address Book, and the occasional low-level function. But if you do, the compiler will point out to you when you need to use a bridging cast.

Note: Not all Objective-C and Core Foundation objects that sound alike are toll-free bridged. For example, CGImage and UIImage cannot be cast to each another, and neither can CGColor and UIColor. This page lists the types that can be used interchangeably.

The __bridge casts are not limited to interactions with Core Foundation. Some APIs take void * pointers that let you store a reference to anything you want, whether that’s an Objective-C object, a Core Foundation object, a malloc()’d memory buffer, and so on. The notation void * means: this is a pointer but the actual datatype of what it points to could be anything.

To convert from an Objective-C object to void *, or the other way around, you will need to do a __bridge cast. For example:

MyClass *myObject = [[MyClass alloc] init];
[UIView beginAnimations:nil context:(__bridge void *)myObject];

In the animation delegate method, you do the conversion in reverse to get your object back:

- (void)animationDidStart:(NSString *)animationID 
  context:(void *)context
{
	MyClass *myObject = (__bridge MyClass *)context;
	. . .
}

We’ll see another example of this later in next chapter, where we cover using ARC with Cocos2D.

To summarize:

  • When changing ownership from Core Foundation to Objective-C you use CFBridgingRelease().
  • When changing ownership from Objective-C to Core Foundation you use CFBridgingRetain().
  • When you want to use one type temporarily as if it were another without ownership change, you use __bridge.

That’s it as far as MainViewController is concerned. All the errors should be gone now and you can build and run the app. We won’t convert AFHTTPRequestOperation to ARC in this tutorial.

For the near future you may find that many of your favorite third-party libraries do not come in an ARC flavor yet. It’s no fun to maintain two versions of a library, one without ARC and one with, so I expect many library maintainers to pick just one. New libraries might be written for ARC only but older ones may prove too hard to convert. Therefore it’s likely that a portion of your code will remain with ARC disabled (the -fno-objc-arc compiler flag).

Fortunately, ARC works on a per-file basis so it’s no problem at all to combine these libraries with your own ARCified projects. Because it’s sometimes a bit of a hassle to disable ARC for a large selection of files, we’ll talk about smarter ways to put non-ARC third-party libraries into your projects in the next chapter.

Contributors

Over 300 content creators. Join our team.