What You’ll Learn in This Hour
Reviewing memory management for objects
Using manual reference counting
Using Automatic Reference Counting
One of the most significant ways in which object-oriented programs and their languages differ from traditional programming is in their use of memory. In older styles of programming, variables were declared in blocks and could be used throughout the block in which they were declared. (Sometimes the entire program was one block.) Variables were placed on the stack when control entered into a block, and when control left the block, the stack was cut back so that the variables were no longer accessible, and their memory was restored to the operating system.
With objects, there is a different pattern of use. It is not the mere declaration of a variable in a block that allocates memory; rather, it is the creation of the object that takes up memory. At the end of the object’s life cycle, the deletion of the object releases its memory.
It is no longer a question of the scope of variables that determines their existence and memory usage but rather the programmer’s creation and destruction of the object. It is true that there are some cases in which an object is created and destroyed in a single block, but often an object is created in one place in a program and destroyed in another.
Objective-C and the Cocoa frameworks have addressed the issue of managing memory for objects in two ways:
Reference counting—Objects carry with them a reference count that represents the number of parties interested in the object. As long as the reference count is greater than zero, the object is deemed to be in use and cannot be deleted. When an interested party is no longer interested in the object, the reference count is decremented by one. When it reaches zero, that signifies that there is no interest in the object and it can be safely deleted.
Garbage collection—At runtime, the interested parties are tracked without the use of reference counters. Periodically, a garbage collection process deletes objects that are no longer needed. Although the code can be highly optimized, it nevertheless adds to the burden on the computer’s resources.
Note
Garbage collection has never been implemented on iOS, and it is now deprecated on OS X. However, you still find references to it in various documents and in old code.
The biggest distinction between these two methods is when they are performed. Reference counting is coded by the developer as the app is being written. Garbage collection happens at runtime, but the developer does not have to worry about managing reference counts.
This is no small consideration because implementing the reference counting code is not particularly complex in the way that managing a multidimensional array can be, but it is remarkably unforgiving. One reference count decrement in the wrong place can let the OS know that it is safe to delete an object when it is not. On the other hand, one reference count increment too many can prevent an object from ever being deleted, and you and your users will have to contend with memory leaks.
With the advent of Mac OS X 10.7 (Lion) and iOS 5, Automatic Reference Counting (ARC) entered the picture. (ARC is pronounced just as the arc of a circle is pronounced.) ARC imposes no runtime burden on the app because it is implemented in the compiler and optimizer. It relies on Cocoa naming conventions to actually generate the code that you had to hand-code in the past to manage reference counting.
Today, reference counting is the basis of memory management for Objective-C. In older apps, it is implemented explicitly using methods of NSObject
and the NSObject
protocol. In modern code, it is implemented with ARC. Accordingly, this hour focuses on reference counting techniques.
ARC implements reference counting in the compiler and optimizer, but you still should understand what it is doing for you. If you are working on older code, you definitely need to understand manual reference counting because that is what you see in your code. Even with new code, not every developer is able to switch over to using ARC, so that is yet another reason for understanding the process.
Because ARC is implementing reference counting behind the scenes, in the unlikely event that something goes wrong, understanding reference counting may help you troubleshoot the problems. However, if you are working only on new code, you can postpone learning about reference counting and just focus on ARC (as described in the following section).
In Hour 14, “Defining a Class in an Implementation File,” you saw how to build a small converter. In Hour 14, you built it with ARC. If you were writing this code in the past (that is, before iOS 5 or Lion), you probably would have written it differently. In the listings that follow, you see comments on the lines that would have been written differently in older versions of the operating systems.
Xcode has turned on ARC starting in the Apple LLVM Compiler 3.0. The settings are in Apple LLVM 5.0 – Language – Objective-C under Build Settings for your project once you have selected your project in the project navigator—and, if necessary, scrolled down to that section. You can also manually adjust the various ARC warnings, as shown in Figure 16.1. There are additional options in Apple LLVM 5.0 – Warnings – Objective C and ARC.
Listing 16.1 shows the interface file as it would have been written before ARC. (Compare this to Figure 14.3 in Chapter 14.) Remember, you should not be writing new code this way. It is presented here because there is still so much pre-ARC code around that you might have to understand, debug, and even modify it.
#import <UIKit/UIKit.h>
@class Converter;
@interface AppDelegate : NSObject <UIApplicationDelegate>
@property (assign) IBOutlet NSWindow *window; //pre-ARC
@property (retain) Converter *currencyConverter; //pre-ARC
@end
The setter attributes of the property declarations in the pre-ARC world can now be described. The property for the IBOutlet
window is set to assign
. This means that a simple assignment statement is used to set the property. The object itself is created at runtime based on your settings in Interface Builder, so it is not an object that your code creates.
When currencyConverter
is set, a retain
message is sent to the object as it is assigned. As described previously, each retain
message must be balanced with a release
message so that the reference count is properly adjusted. The property declaration as shown here provides the retain
.
There is a great deal of material on developer.apple.com covering memory management. In particular, you will find examples and discussion of retain cycles. The most common case of a retain cycle is that in which two objects each retain the other.
“Advanced Memory Management Programming Guide” on developer.apple.com provides a thorough guide to what happens. It summarizes what you need to know in three points:
“You own any object you create [...] using a method whose name starts with alloc
, new
, copy
, or mutableCopy
.” You do not need to retain these objects because you already own them.
“You can take ownership of an object using retain.” The object will not be deleted as long as you continue to own it, and that leads to the final point...
“You must release any object when you no longer need it. If you do not, it will not be deallocated and its memory will continue to be held in use. Note that you release an object when you no longer need it. Releasing it in no way suggests that no one else needs it—other parties release it when they are done with it.”
Managing memory in this way is not very complicated conceptually. However, as you will see from the volume of information and discussion on developer.apple.com, this form of memory management is labor and detail intensive. Automatic Reference Counting addresses those issues.
When ARC is turned on, the compiler and optimizer implement the needed retain
, release
, and dealloc
code. In fact, you are not allowed to use them. For pre-ARC projects, you can turn ARC on; however, that can break existing code.
You might not notice it at first, but as soon as you clean a project where you have turned on ARC and built it again, you have errors. For this reason, you are safer using the Edit, Refactor, Convert to Objective-C ARC command for legacy projects. You will have a chance to compare the before and after versions of changed files, and Xcode will normally take care of problems.
Note, however, that there are a few circumstances in which Xcode cannot do the ARC conversion. At least in this author’s experience, Xcode handles most conversions well; and if it can’t do the conversion, it advises you. There are few if any reports of faulty conversions to ARC.
Using Cocoa Coding Conventions to Take Advantage of ARC
ARC relies on the Cocoa coding conventions—particularly the convention that a method name begins with a verb. The following verbs cause ARC to retain the object that is returned: alloc
, init
, new
, and copy
. Thus, a method called prepareMyObjectAndCopy
does not trigger an ARC retain
(and it also is a bad method name for other reasons).
There is another issue for you to confront when you are using ARC and iOS 5 or later along with Mac OS X 10.7 (Lion) or later. There are some modifications to declared property attributes. Property attributes before these versions included the following types and values:
Accessor method names—You can specify methods to use for getter and setter if you want to write your own accessors. If you do not use your own accessors, they are created for you with the default names.
Writeability—The values are readwrite
and readonly
.
Setter semantics—These determine what happens when the property is set. Until iOS 5 and Mac OS X 10.7, the only values were assign
, retain
, and copy
. There are additional values that are described in the following section.
Atomicity—If your accessors are nonatomic, you can use the nonatomic
keyword. (There is not a comparable atomic setting; it is the default.)
The setter semantic values make sense in the world of ARC, and they remain available to you. This avoids breaking existing code, but it means that you have to think about the memory management and reference counting that the compiler now implements for you. Two new keywords listed below are available to let you avoid thinking about the details of reference counting:
strong—A synonym for retain
. It describes the link to the object rather than the action to take when setting the property. Do not worry: It is a true synonym and a retain
message is going to be generated, but when you think about strong
, you are thinking about the link to the object and letting the compiler handle the mechanics of implementing it.
weak—Similar to assign
, but it also focuses on the nature of the link to the object rather than on the mechanics of implementation. It is not exactly the same as assign
because it implements a significant additional feature.
In pre-ARC days, it was very common to wind up with dangling pointers to objects. If you have a property that points to an object, and if that object is deallocated, the property still points to where the object was.
The best practice was to set the property to nil
immediately after releasing it so that the pointer was safe. Remember that sending a message to a nil
object is not an error.
With the weak
keyword, if the object that the property points to is deallocated, the property is automatically set to nil
. This saves a great deal of time that previously was spent debugging crashes. It also might prevent some memory leaks that were caused by people being hesitant to release objects. This was never a good programming practice, but it was definitely used in many cases.
Caution
An additional keyword, unsafe_unretained
, implements exactly the old assign
functionality. However, its use is discouraged because you might wind up with a dangling pointer.
strong
and weak
are not required for ARC; you can continue using assign
and retain
. However, over time, assign
and retain
will probably wind up requiring more explanation than strong
and weak
as the details of manual reference counting for memory management slip into the past. Using strong
and weak
makes the code more readable.
If you are using variables directly (as opposed to properties), you can attach qualifiers to them. In most cases, you do not need to use them because the default (__strong
) is correct in most cases. These are the variable qualifiers available with ARC. Note that each one starts with two underscore characters.
__strong—This is the default value for variables, so you do not need to specify it.
__weak—This is used for a reference to an object that may disappear—exactly as is the case with the setter semantic for declared properties. If the object disappears, the variable is set to nil
.
__unsafe_unretained—This self-explanatory qualifier is used to reference objects that might not be zeroed out with nil
when the object disappears.
__autoreleasing—This is a part of a new implementation of autoreleasing
that is described in the next section.
The retain
message is processed immediately when it is sent. Until you send a release
message, you own the object you have retained, and you can rely on it being present.
Tip
Your ownership might not be unique because there may be other owners. Each one is guaranteed that the object will be there until the owner releases it. It might be there longer if other owners still own it, but once you have released your ownership, you don’t care.
As you saw in Listing 16.1, it is common to release a number of objects at the same time when you are finished with them. In many cases, your retain
messages are scattered through your code as you encounter the need for them, but you may release them at a known location (such as a dealloc
method). The NSAutoreleasePool
class lets you add to-be-released objects to a pool from which they are all released at the same time. This is particularly useful in tight loops where the objects are being retained and released in rapid succession.
Before ARC, this was implemented with a class. Your code would look like this:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init;
// code in which you retain objects
[pool release];
Once you have a pool, you add objects to it as you create them. NSObject
protocol has an autorelease
method that adds that object to the current autorelease
pool. It’s typically used in this way:
[[[SomeObject alloc] init] autorelease]
You no longer have to deal with autorelease
pools directly. You use a compiler directive that is similar to a try
block to delimit the pool. The syntax is quite simple: Use the @autoreleasepool
directive and place the code for the objects to be autoreleased
in brackets. Here is how the previous code is now written:
@autoreleasepool {
// code in which you retain objects
}
A frequent use of an @autoreleasepool
block is to delimit code in which a large number of objects are created. On exit from the block, they are released. ARC would get around to doing so, but this can speed up the process and reduce the memory footprint.
Before ARC, you had to manually take care of memory management for the objects you created. In this chapter, you find a brief overiew of how memory management is implemented with reference counting as well as a discussion of how ARC automates that process for you.
Q. What are the most common problems that result from manual reference counting?
A. Memory leaks and app crashes are the most common problems that result from manual reference counting. Be thankful for ARC!
Q. Why should you think twice about converting existing code to ARC?
A. Because constructs such as retain
, release
, and dealloc
are not allowed under ARC, you may break existing code.
1. Why are the Cocoa coding conventions so important with ARC?
2. What is the difference between garbage collection and reference counting?
1. The initial verb in a method name (alloc
, init
, new
, or copy
) triggers ARC to retain an object.
2. Garbage collection is a process that runs at runtime. Reference counting is implemented when the code is written and compiled. Its effects are seen at runtime.
Adding ARC to an existing Xcode project can break it, as pointed out in this hour. However, it is worth your time to experiment with an old project to see how much damage is done. There are projects that have little damage by adopting ARC. These can be projects that you (or someone else) might have written when you were first starting out. If a project totally ignores memory management, it might leak like a sieve, but for some small projects that doesn’t matter. In a case such as that, moving to ARC plugs the leaks with minimal effort on your part. Just remember: When you switch the ARC settings, clean your project before rebuilding it. And, of course, work in a copy of the code.
18.226.177.85