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.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Beginning ARC in iOS 5 Tutorial Part 2
55 mins
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 Matthijs Hollemans, an experienced iOS developer and designer.
This is the second part of a two part tutorial series on using ARC in iOS 5.
In the first part of the series, we covered how ARC works and how to convert projects to use ARC with Xcode’s built in conversion utility.
In this second and final part of the series, we’ll cover how to convert files by hand, ARC as it relates to Core Foundation, weak properties, and much more!
Converting By Hand
We’ve converted almost the entire project to ARC already, except for MainViewController and AFHTTPRequestOperation. In this section I’ll show you how to convert MainViewController by hand. Sometimes it’s fun to do things yourself so you get a better feel for what truly happens.
If you look at MainViewController.h you’ll see that the class declares two instance variables:
@interface MainViewController : UIViewController
<UITableViewDataSource, UITableViewDelegate, UISearchBarDelegate,
NSXMLParserDelegate>
{
NSOperationQueue *queue;
NSMutableString *currentStringValue;
}
When you think about it, the public interface of a class is a strange place to put instance variables. Usually, instance variables really are a part of the internals of your class and not something you want to expose in its public interface. To a user of your class it isn’t very important to know what the class’s instance variables are. From the perspective of data hiding it is better if we move such implementation details into the @implementation section of the class. I’m happy to say that this is now possible with LLVM 3.0 (whether you use ARC or not).
Remove the instance variable block from MainViewController.h and put it into MainViewController.m. The header file should now looks like:
#import <UIKit/UIKit.h>
@interface MainViewController : UIViewController
<UITableViewDataSource, UITableViewDelegate, UISearchBarDelegate,
NSXMLParserDelegate>
@property (nonatomic, retain) IBOutlet UITableView *tableView;
@property (nonatomic, retain) IBOutlet UISearchBar *searchBar;
@end
And the top of MainViewController.m looks like:
@implementation MainViewController
{
NSOperationQueue *queue;
NSMutableString *currentStringValue;
}
Build the app and… it just works. This makes your .h files a lot cleaner and puts the ivars where they really belong.
You can do the same for the SoundEffect class. Simply move the instance variable section into the .m file. Because we now don’t reference the SystemSoundID symbol anywhere in SoundEffect.h, you can also move the #import for AudioServices.h into SoundEffect.m. The SoundEffect header no longer exposes any details of its implementation. Nice and clean.
Note: You can also put instance variables in class extensions. This is useful for when the implementation of your class is spread over multiple files. You can then put the extension in a shared, private header file, so that all these different implementation files have access to the instance variables.
It’s time to enable ARC on MainViewController.m. Go into the Build Phases settings and remove the -fno-objc-arc compiler flag from MainViewController.m. You may have some problems getting Xcode to recognize this. Try doing a new build. You should get a ton of errors but if Xcode still says “Build Succeeded”, then close the project and reopen it.
Dealloc
Let’s go through these errors and fix them one by one. We begin with dealloc:
Every single line in dealloc gives an error. We’re not supposed to call [release] anymore, nor [super dealloc]. Because we’re not doing anything else in dealloc, we can simply remove the entire method.
The only reason for keeping a dealloc method around is when you need to free certain resources that do not fall under ARC’s umbrella. Examples of this are calling CFRelease() on Core Foundation objects, calling free() on memory that you allocated with malloc(), unregistering for notifications, invalidating a timer, and so on.
Sometimes it is necessary to explicitly break a connection with an object if you are its delegate but usually this happens automatically. Most of the time delegates are weak references (something we will get into soon) so when the object to be deallocated is someone else’s delegate, the delegate pointer will be set to nil automatically when the object is destroyed. Weak pointers clean up after themselves.
By the way, in your dealloc method you can still use your instance variables because they haven’t been released yet at that point. That doesn’t happen until after dealloc returns.
The SoundEffect Getter
The soundEffect method calls release, so that’s an easy fix:
This method is actually the getter method for the soundEffect property. It employs a lazy loading technique to load the sound effect the first time it is used. I used a common pattern here for creating objects under manual memory management. First the new object is stored in a temporary local variable, then it is assigned to the actual property, and finally the value from the local variable is released. This is how I used to write this sort of thing and you may have been doing it too:
SoundEffect *theSoundEffect = [[SoundEffect alloc]
initWithSoundNamed:@"Sound.caf"];
self.soundEffect = theSoundEffect;
[theSoundEffect release];
We could just remove the call to release and leave it at that, but now having a separate local variable isn’t very useful anymore:
SoundEffect *theSoundEffect = [[SoundEffect alloc]
initWithSoundNamed:@"Sound.caf"];
self.soundEffect = theSoundEffect;
So instead, we can simplify it to just one line:
self.soundEffect = [[SoundEffect alloc] initWithSoundNamed:
@"Sound.caf"];
Under manual memory management this would cause a leak (there is one retain too many going on) but with ARC this sort of thing is just fine.
Please Release Me, Let Me Go
Just like you can’t call release anymore, you also cannot call autorelease:
The fix is straightforward. Instead of doing,
cell = [[[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier] autorelease];
This line becomes:
cell = [[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
The next method that has errors is escape:, but we’ll skip it just for a second. These issues are related to toll-free bridging, a topic that I dedicate a special section to.
The remaining two errors are releases, in searchBarSearchButtonClicked: and in parser:didEndElement:. You can simply remove these two lines.