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
Properties
If you look at the top of MainViewController.m, you’ll see that it uses a class extension to declare two private properties, searchResults and soundEffect:
@interface MainViewController ()
@property (nonatomic, retain) NSMutableArray *searchResults;
@property (nonatomic, retain) SoundEffect *soundEffect;
@end
This is done primarily to make manual memory management easier and it’s a common reason why developers use properties. When you do,
self.searchResults = [NSMutableArray arrayWithCapacity:10];
the setter will take care of releasing the old value (if any) and properly retaining the new value. Developers have been using properties as a way of having to think less about when you need to retain and when you need to release. But now with ARC you don’t have to think about this at all!
In my opinion, using properties just for the purposes of simplifying memory management is no longer necessary. You can still do so if you want to but I think it’s better to just use instance variables now, and only use properties when you need to to make data accessible to other classes from your public interface.
Therefore, remove the class extension and the @synthesize statements for searchResults and soundEffect. Add new instance variables to replace them:
@implementation MainViewController
{
NSOperationQueue *queue;
NSMutableString *currentStringValue;
NSMutableArray *searchResults;
SoundEffect *soundEffect;
}
Of course, this means we can no longer do self.searchResults and self.soundEffect. Change viewDidUnload to the following:
- (void)viewDidUnload
{
[super viewDidUnload];
self.tableView = nil;
self.searchBar = nil;
soundEffect = nil;
}
We still need to set soundEffect to nil because we do want to deallocate the SoundEffect object here. When the iPhone gets a low-memory warning, we should free as much memory as possible and the SoundEffect object is expendable at that point. Because instance variables create strong relationships by default, setting soundEffect to nil will remove the owner from the SoundEffect object and it will be deallocated immediately.
The soundEffect method now becomes:
- (SoundEffect *)soundEffect
{
if (soundEffect == nil) // lazy loading
{
soundEffect = [[SoundEffect alloc]
initWithSoundNamed:@"Sound.caf"];
}
return soundEffect;
}
That’s about as simple as we can make it. The SoundEffect object is allocated and assigned to the soundEffect instance variable. This variable becomes its owner and the object will stay alive until we set soundEffect to nil (in viewDidUnload), or until the MainViewController is deallocated.
In the rest of the file, replace anywhere where it says self.searchResults with just searchResults. When you build the app again, the only errors it should give are on the escape: method.
Note that in searchBarSearchButtonClicked, we still do:
[self.soundEffect play];
This will work even though we no longer have a property named soundEffect. The dot syntax isn’t restricted to just properties, although that’s what it is most commonly used for. If using dot syntax here offends you, you can change this line to:
[[self soundEffect] play];
Don’t change it to this, though:
[soundEffect play];
Because we use lazy loading to load the SoundEffect object, the soundEffect instance variable will always be nil until you call the soundEffect method. Therefore, to be certain we actually have a SoundEffect object, you should always access it through self. If you feel that this pattern does morally require you to declare soundEffect as a @property, then go right ahead. Different strokes for different folks. :-)
As a best practice, if you define something as a property, then you should always use it as a property. The only places where you should access the property’s backing instance variable directly are in init and when you provide a custom getter and setter. Anywhere else you should access the property through self.propertyName. That is why synthesize statements often rename the ivar:
@synthesize propertyName = _propertyName;
This construct will prevent you from accidentally using the backing instance variable by typing “propertyName” when you meant to use “self.propertyName”.
Speaking of properties, MainViewController still has two outlet properties in its .h file:
@property (nonatomic, retain) IBOutlet UITableView *tableView;
@property (nonatomic, retain) IBOutlet UISearchBar *searchBar;
The retain keyword for properties still works with ARC and is simply a synonym for strong. However, it is better to call your properties strong because that’s the proper term from now on. But for these two particular properties I have other plans. Instead of strong, we will declare them as weak:
@property (nonatomic, weak) IBOutlet UITableView *tableView;
@property (nonatomic, weak) IBOutlet UISearchBar *searchBar;
Weak is the recommended relationship for all *outlet* properties. These view objects are already part of the view controller’s view hierarchy and don’t need to be retained elsewhere. The big advantage of declaring your outlets weak is that it saves you time writing the viewDidUnload method.
Currently our viewDidUnload looks like this:
- (void)viewDidUnload
{
[super viewDidUnload];
self.tableView = nil;
self.searchBar = nil;
soundEffect = nil;
}
You can now simplify it to the following:
- (void)viewDidUnload
{
[super viewDidUnload];
soundEffect = nil;
}
That’s right, because the tableView and searchBar properties are weak, they are automatically set to nil when the objects they point to are destroyed. That’s why we call them “zeroing” weak pointers.
When the iPhone receives a low-memory warning, the view controller’s main view gets unloaded, which releases all of its subviews as well. At that point the UITableView and UISearchBar objects cease to exist and the zeroing weak pointer system automatically sets self.tableView and self.searchBar to nil. There is no more need to do this ourselves in viewDidUnload. In fact, by the time viewDidUnload gets called these properties already are nil. I shall demonstrate this soon, but for that we need to add a second screen to the application.
This doesn’t mean you can completely forget about viewDidUnload. Remember, as long as you keep a pointer to an object, it stays alive. If you no longer want to hang on to objects, you need to set their pointers to nil. That’s exactly what we do for soundEffect. We don’t want to delete the searchResults array at this point but if we did then we would also set that to nil here. Any data that you don’t need that isn’t a weak outlet property you still need to nil out in viewDidUnload! The same goes for didReceiveMemoryWarning.
So from now on, make your outlet properties weak. The only outlets that should be strong are the ones from File’s Owner that are connected to top-level objects in the nib.
To summarize, the new modifiers for properties are:
- strong. This is a synonym for the old “retain”. A strong property becomes an owner of the object it points to.
- weak. This is a property that represents a weak pointer. It will automatically be set to nil when the pointed-to object is destroyed. Remember, use this for outlets.
-
unsafe_unretained. This is a synonym for the old “assign”. You use it only in exceptional situations and when you want to target iOS 4. More about this later.
copy. This is still the same as before. It makes a copy of the object and creates a strong relationship. - assign. You’re no longer supposed to use this for objects, but you still use it for primitive values such as BOOL, int, and float.
Before ARC, you were able to write this:
@property (nonatomic, readonly) NSString *result;
which would implicitly create an assign property. That used to be fine for readonly values. After all, what does retained mean when you talk about read-only data? However, with ARC the above will give the following error:
"ARC forbids synthesizing a property of an Objective-C object with unspecified ownership or storage attribute"
You must explicitly state whether you want this property to be strong, weak or unsafe_unretained. Most of the time, strong is the proper answer:
@property (nonatomic, strong, readonly) NSString *result;
Earlier I mentioned that if you declare a property you should always access it through self.propertyName rather than through the backing instance variable (except in init and in any custom getter or setter methods). This is especially true for readonly properties. ARC can get confused if you modify such properties by changing their instance variables and strange bugs will result. The correct way is to redefine the property as readwrite in a class extension.
In your .h:
@interface WeatherPredictor
@property (nonatomic, strong, readonly) NSNumber *temperature;
@end
In your .m:
@interface WeatherPredictor ()
@property (nonatomic, strong, readwrite) NSNumber *temperature;
@end