Beginning ARC in iOS 5 Tutorial Part 2
http://www.raywenderlich.com/5773/beginning-arc-in-ios-5-tutorial-part-2
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!
@implementation MainViewController { NSOperationQueue *queue; NSMutableString *currentStringValue; }
vs class extension
We’re not supposed to call [release] anymore, nor [super dealloc].
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.
- (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 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.
@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
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.-
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.
Any data that you don’t need that isn’t a weak outlet property you still need to nil out in viewDidUnload!
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
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 |
++ Toll-Free Bridging
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);
|
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.
the constant string, which requires no bridging cast because it is a special object that will never be released
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
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.
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??
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.
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)); } |
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.
Anywhere you call a Core Foundation function named Create, Copy, or Retain you must do CFBridgingRelease() to safely transfer the value to ARC.
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); |
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); |
Core Graphics (which doesn’t have any toll-free bridged types),
The notation void * means: this is a pointer but the actual datatype of what it points to could be anything.
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.
Delegates and Weak Properties
If screen A invokes screen B, and screen B needs to tell A something — for example, that it needs to close — you make A a delegate of B.
#import <UIKit/UIKit.h> @class DetailViewController; @protocol DetailViewControllerDelegate <NSObject> - (void)detailViewController:(DetailViewController *)controller didPickButtonWithIndex:(NSInteger)buttonIndex; @end @interface DetailViewController : UIViewController @property (nonatomic, weak) id <DetailViewControllerDelegate> delegate; - (IBAction)coolAction; - (IBAction)mehAction; @end
The MainViewController creates the DetailViewController and presents it on the screen. That gives it a strong reference to this object. The DetailViewController in turn has a reference to a delegate.
So here we have a situation where two objects point at each other:
Making the delegate pointer weak is necessary to prevent ownership cycles.
retain cycle, where two objects retain each other so neither will ever be deallocated. That’s a common form of memory leak.
In systems that employ garbage collection (GC) to handle their memory management, the garbage collector can recognize such cycles and release them anyway. But ARC is not garbage collection and for dealing with ownership cycles you’re still on your own. The weak pointer is an important tool for breaking such cycles.
Therefore, the rule is that delegates should be declared weak. Most of the time your properties and instance variables will be strong, but this is the exception.
Still, it’s a good idea to stick to the recommended pattern:
- parent pointing to a child: strong
- child pointing to a parent: weak
+
@property (nonatomic, strong) NSString *artistName; @property (nonatomic, weak) IBOutlet UINavigationBar *navigationBar; |
The artistName property will contain the name of the selected artist. Previously you would have made this a retain property (or copy), so now it becomes strong.
The navigationBar property is an outlet. As before, outlets that are not top-level objects in the nib should be made weak so they are automatically released in low-memory situations.
Don’t forget to connect the navigation bar from the nib file to the outlet!???
+
@property (nonatomic, copy) NSString *artistName; |
By adding the copy modifier, it makes it so that when we assign to the property like this:
controller.artistName = artistName; |
the app first makes a copy of the string object from the local variable and then stores that copy into the property. ???
+
- (void)viewDidUnload { [super viewDidUnload]; NSLog(@"tableView %@", self.tableView); NSLog(@"searchBar %@", self.searchBar); soundEffect = nil; }
Artists[880:207] Received memory warning.
Artists[880:207] tableView (null)
Artists[880:207] searchBar (null)
Because tableView and searchBar are weak properties, these objects are only owned by the view hierarchy. As soon as the main view gets unloaded, it releases all its subviews. Because we don’t hold on to the UITableView and UISearchBar objects with strong pointers, these objects get deleted before viewDidUnload is invoked.
It’s not necessarily wrong to make outlets strong but then you accept responsibility for setting these properties to nil by hand in viewDidUnload.
++Unsafe_unretained
The reason this new modifier has the word “unsafe” in its name is that it can point to an object that no longer exists.
Technically speaking, if you don’t use any unsafe_unretained properties or variables, you can never send messages to deallocated objects anymore.
The reason unsafe_unretained still exists is for compatibility with iOS 4, where the weak pointer system is not available, and for a few other tricks.
Without this setting, the app may not have crashed at all, or it may have crashed at some later point.
For ARC-enabled apps the Enable Zombie Objects setting (also known as NSZombieEnabled) isn’t terribly useful anymore, so you can disable it… except when you’re using unsafe_unretained pointers!
Using ARC on iOS 4
You cannot use weak or __weak on iOS 4, so replace weak properties with unsafe_unretained and __weak variables with __unsafe_unretrained.
Remember that these variables aren’t set to nil when the referenced object is deallocated, so if you’re not careful your variables may be pointing at objects that no longer exist. Be sure to test your app with NSZombieEnabled!
Because ARC is largely a new feature of the LLVM 3.0 compiler
posted on 2013-01-24 04:38 Chansonyan 阅读(180) 评论(0) 收藏 举报