iOS Programming: The Big Nerd Ranch Guide (2014)

3. Managing Memory with ARC

In this chapter, you will learn how memory is managed in iOS and the concepts that underlie automatic reference counting, or ARC. Let’s start with some basics of application memory.

The Stack

When a method (or function) is executed, a chunk of memory is allocated from a part of memory called the stack. This chunk of memory is called a frame, and the frame stores the values for variables declared inside the method. A variable declared inside a method is called a local variable.

When an application launches and runs main(), the frame for main() is put on the stack. When main() calls another method (or function), the frame for that method is put on top of the stack. Of course, that method could call another method, and so on, until you have a towering stack of frames. As each method or function finishes, its frame is “popped off” the top of the stack and destroyed. If the method is called again, a new frame will be allocated and put on the stack.

For example, in the RandomItems application, the main function runs BNRItem’s randomItem method, which in turn runs alloc. The stack would look like Figure 3.1. Notice that main()’s frame stays alive while the other methods are executing because it has not yet finished executing.

Figure 3.1  Stack growing and shrinking

Stack growing and shrinking

The randomItem method runs inside a loop in main(). With every iteration of the loop, the stack grows and shrinks as frames are put on and popped off the stack.

The Heap

There is another part of memory called the heap that is separate from the stack. The reason for the names “heap” and “stack” has to do with how you visualize them. The stack can be visualized as an orderly stack of frames. The heap, on the other hand, is where all Objective-C objects live. It is a giant heaping mess of objects. You use pointers to keep track of where those objects are stored in the heap.

When you send the alloc message to a class, a chunk of memory is allocated from the heap. This chunk is your object, and it includes space for the object’s instance variables. An instance of BNRItem has five instance variables: four pointers (isa, _itemName, _serialNumber, and _dateCreated) and an int (_valueInDollars). Thus, the chunk of memory that is allocated includes space for one int and four pointers. These pointers store addresses of other objects in the heap.

An iOS application creates objects at launch and will typically continue to create objects for as long as the application is running. If heap memory were infinite, the application could create all the objects that it wanted to and have them exist for the entire run of the application.

But an application gets only so much heap memory, and memory on an iOS device is especially limited. Thus, this resource must be managed: It is important to destroy objects that are no longer needed to free up heap memory so that it can be reused to create new objects. On the other hand, it is critical not to destroy objects that are still needed.

ARC and memory management

The good news is that you do not need to keep track of which objects should live and die. Your application’s memory management is handled for you by ARC, which stands for Automatic Reference Counting. All of the applications in this book will use ARC. Before ARC was available, applications used manual reference counting. There is more information about manual reference counting at the end of the chapter.

ARC can be relied on to manage your application’s memory automatically for the most part. However, it is important to understand the concepts behind it to know how to step in when you need to. Let’s start with the idea of object ownership.

Pointer Variables and Object Ownership

Pointer variables imply ownership of the objects that they point to.

·         When a method (or function) has a local variable that points to an object, that variable is said to own the object being pointed to.

·        When an object has an instance variable that points to another object, the object with the pointer is said to own the object being pointed to.

Think back to your RandomItems application. In this application, an instance of NSMutableArray is created in main() and then ten BNRItem instances are added to it. Figure 3.2 shows some of the objects in RandomItems and the pointers that reference them.

Figure 3.2  RandomItems object diagram (with only two items)

RandomItems object diagram (with only two items)

Within main(), the local variable items points to an instance of NSMutableArray, so main() owns that NSMutableArray instance.

The array, in turn, owns the BNRItem instances. A collection object, like an instance of NSMutableArray, holds pointers to objects instead of actually containing them, and these pointers imply ownership: an array always owns the objects that are “in” the array.

Finally, each BNRItem instance owns the objects pointed to by its instance variables.

The idea of object ownership is useful for determining whether an object will be destroyed so that its memory can be reused.

·         An object with no owners will be destroyed. An ownerless object cannot be sent messages and is isolated and useless to the application. Keeping it around wastes precious memory. This is called a memory leak.

·         An object with one or more owners will not be destroyed. If an object is destroyed but another object or method still has a pointer to it (or, more accurately, a pointer that stores the address where the object used to live), then you have a dangerous situation: sending a message via this pointer may crash your application. Destroying an object that is still needed is called premature deallocation. It is also known as a dangling pointer or a dangling reference.

How objects lose owners

Here are the ways that an object can lose an owner:

·        A variable that points to the object is changed to point to another object.

·        A variable that points to the object is set to nil.

·        The owner of the object is itself destroyed.

·        An object in a collection, like an array, is removed from that collection.

Let’s take a look at each of these situations.

Changing a pointer

Imagine an instance of BNRItem. Its _itemName instance variable points to an NSString instance @"RustySpork". If you polished the rust off that spork, it would become a shiny spork, and you would want to change the _itemName to point at a different NSString.

Changing a pointer

When the value of _itemName changes from the address of the “Rusty Spork” string to the address of the “Shiny Spork” string, the “Rusty Spork” string loses an owner. If it has no other owners, then it will be destroyed.

Setting a pointer to nil

Setting a pointer to nil represents the absence of an object. For example, say you have a BNRItem instance that represents a television. Then, someone scratches off the television’s serial number. You would set its _serialNumber instance variable to nil. The NSString instance that _serialNumberpreviously pointed to loses an owner.

The owner is destroyed

When an object is destroyed, the objects that it owns lose an owner. In this way, one object being deallocated can cause a cascade of object deallocations.

Through its local variables, a method or a function can own objects. When the method or function is done executing and its frame is popped off the stack, the objects it owns will lose an owner.

Removing an object from a collection

There is one more important way an object can lose an owner. An object in a collection object is owned by the collection object. When you remove an object from a mutable collection object, like an instance of NSMutableArray, the removed object loses an owner.

[items removeObject:item];    // Object pointed to by item loses an owner

Keep in mind that losing an owner by any of these means does not necessarily result in the object being destroyed; if there is still another pointer to the object somewhere, then the object will continue to exist. When an object loses its last owner, the result is certain and appropriate death.

Ownership chains

Because objects own objects, which can own other objects, the destruction of a single object can set off a chain reaction of loss of ownership, object destruction, and freeing up of memory.

There is an example of this in RandomItems. Take another look at the object diagram for this application.

Figure 3.3  Objects and pointers in RandomItems

Objects and pointers in RandomItems

In main.m, after you finish printing out the array, you set the items variable to nil. Setting items to nil causes the array to lose its only owner, so the array is destroyed.

But the destruction does not stop there. When the array is destroyed, all of the pointers to the BNRItem instances are destroyed. Once these variables are gone, no one owns any of the items, so they are destroyed.

Finally, destroying a BNRItem destroys its instance variables, which leaves the objects pointed to by those variables unowned. So they, too, get destroyed.

Let’s add some code so that you can see this destruction as it happens. NSObject implements a dealloc method, which is sent to an object just before it is destroyed. You can override dealloc in BNRItem to print something to the console when an item is destroyed.

In the RandomItems project, open BNRItem.m and override dealloc.

- (void)dealloc

{

    NSLog(@"Destroyed: %@", self);

}

In main.m, add the following line of code.

NSLog(@"Setting items to nil...");

items = nil;

Build and run the application. After the items print out, you will see the message announcing that items is being set to nil. Then, you will see the destruction of each BNRItem logged to the console.

At the end, there are no more objects taking up memory, and only the main function remains. All this automatic clean-up and memory recycling occurs as the result of setting items to nil. That is the power of ARC.

Strong and Weak References

We have said that anytime a pointer variable points to an object, that object has an owner and will stay alive. This is known as a strong reference.

A variable can optionally not take ownership of an object that it points to. A variable that does not take ownership of an object is known as a weak reference.

A weak reference is useful for preventing a problem called a strong reference cycle (also known as a retain cycle.) A strong reference cycle occurs when two or more objects have strong references to each other. This is bad news. When two objects own each other, they can never be destroyed by ARC. Even if every other object in the application releases ownership of these objects, these objects (and any objects that they own) will continue to exist inside their bubble of mutual ownership.

Thus, a strong reference cycle is a memory leak that ARC needs your help to fix. You fix it by making one of the references weak.

Let’s introduce a strong reference cycle in RandomItems to see how this works. First, you are going to give an instance of BNRItem the ability to hold another BNRItem (to represent something like a backpack or a purse). In addition, an item will know which other item holds it.

In BNRItem.h, declare two instance variables and their accessors.

@interface BNRItem : NSObject

{

    NSString *_itemName;

    NSString *_serialNumber;

    int _valueInDollars;

    NSDate *_dateCreated;

    BNRItem *_containedItem;

    BNRItem *_container;

}

+ (instancetype)randomItem;

- (instancetype)initWithItemName:(NSString *)name

                  valueInDollars:(int)value

                    serialNumber:(NSString *)sNumber;

- (instancetype)initWithItemName:(NSString *)name;

- (void)setContainedItem:(BNRItem *)item;

- (BNRItem *)containedItem;

- (void)setContainer:(BNRItem *)item;

- (BNRItem *)container;

In BNRItem.m, implement the accessors.

- (void)setContainedItem:(BNRItem *)item

{

    _containedItem = item;

    // When given an item to contain, the contained

    // item will be given a pointer to its container

    item.container = self;

}

- (BNRItem *)containedItem

{

    return _containedItem;

}

- (void)setContainer:(BNRItem *)item

{

    _container = item;

}

- (BNRItem *)container

{

    return _container;

}

In main.m, remove the code that populates the array with random items. Then create two new items, add them to the array, and make them point at each other.

int main (int argc, const char * argv[])

{

    @autoreleasepool {

        NSMutableArray *items = [[NSMutableArray alloc] init];

        for (int i = 0; i < 10; i++) {

            BNRItem *item = [BNRItem randomItem];

            [items addObject:item];

        }

        BNRItem *backpack = [[BNRItem alloc] initWithItemName:@"Backpack"];

        [items addObject:backpack];

        BNRItem *calculator = [[BNRItem alloc] initWithItemName:@"Calculator"];

        [items addObject:calculator];

        backpack.containedItem = calculator;

        backpack = nil;

        calculator = nil;

        for (BNRItem *item in items)

            NSLog(@"%@", item);

        NSLog(@"Setting items to nil...");

        items = nil;

    }

    return 0;

}

Here is what the application looks like now:

Figure 3.4  RandomItems with strong reference cycle

RandomItems with strong reference cycle

Build and run the application. This time, you will not see any messages reporting the destruction of the BNRItem objects. This is a strong reference cycle: the backpack and the calculator have strong references to one another, so there is no way to destroy these objects. Figure 3.5 shows the objects in the application that are still taking up memory once items has been set to nil.

Figure 3.5  Memory leak!

Memory leak!

The two items cannot be accessed by any other part of the application (in this case, main()), yet they still exist, doing nothing useful. Moreover, because they cannot be destroyed, neither can the objects that their instance variables point to.

To fix this problem, one of the pointers between the items needs to be a weak reference. To decide which one should be weak, think of the objects in the cycle as being in a parent-child relationship. In this relationship, the parent can own its child, but a child should never own its parent. In our strong reference cycle, the backpack is the parent, and the calculator is the child. Thus, the backpack can keep its strong reference to the calculator (the _containedItem instance variable), but the calculator’s reference to the backpack (the _container instance variable) should be weak.

To declare a variable as a weak reference, you use the __weak attribute. In BNRItem.h, change the container instance variable to be a weak reference.

__weak BNRItem *_container;

Build and run the application again. This time, the objects are destroyed properly.

Most strong reference cycles can be broken down into a parent-child relationship. A parent typically keeps a strong reference to its child, so if a child needs a pointer to its parent, that pointer must be a weak reference to avoid a strong reference cycle.

A child holding a strong reference to its parent’s parent also causes a strong reference cycle. So the same rule applies in this situation: if a child needs a pointer to its parent’s parent (or its parent’s parent’s parent, etc.), then that pointer must be a weak reference.

Apple’s development tools includes a Leaks tool to help you find strong reference cycles. You will see how to use this tool in Chapter 14.

A weak reference knows when the object that it points to is destroyed and responds by setting itself to nil. Thus, if the backpack is destroyed, the calculator’s _container instance variable will be automatically set to nil. This is convenient. If _container was not set to nil, then destroying the object would leave you with a dangling pointer, which could crash your application.

Here is the current diagram of RandomItems. Notice that the arrow representing the container pointer variable is now a dotted line. A dotted line denotes a weak reference. Strong references are always solid lines.

Figure 3.6  RandomItems with strong reference cycle avoided

RandomItems with strong reference cycle avoided

Properties

Each time you have declared an instance variable in BNRItem, you have declared and implemented a pair of accessor methods. Now you are going to learn to use properties, a convenient alternative to writing out accessors methods that saves a lot of typing and makes your class files easier to read.

Declaring properties

A property declaration has the following form:

@property NSString *itemName;

By default, declaring a property will get you three things: an instance variable and two accessors for the instance variable. Take a look at Table 3.1, which shows a class not using properties on the left and the equivalent class with properties on the right.

Table 3.1  With and without properties

 

Without properties

With properties

BNRThing.h

@interface BNRThing : NSObject

{

    NSString *_name;

}

- (void)setName:(NSString *)n;

- (NSString *)name;

@end

@interface BNRThing : NSObject

@property NSString *name;

@end

BNRThing.m

@implementation BNRThing

- (void)setName:(NSString *)n

{

    _name = n;

}

- (NSString *)name

{

    return _name;

}

@end

@implementation BNRThing

@end

These two classes in Table 3.1 are exactly the same: each has one instance variable for the name of the instance and a setter and getter for the name. On the left, you type out these declarations and instance variables yourself. On the right, you simply declare a property.

You are going to replace your instance variables and accessors in BNRItem with properties.

In BNRItem.h, delete the instance variable area and the accessor method declarations. Then, add the property declarations that replace them.

@interface BNRItem : NSObject

{

    NSString *_itemName;

    NSString *_serialNumber;

    int _valueInDollars;

    NSDate *_dateCreated;

    BNRItem *_containedItem;

    __weak BNRItem *_container;

}

@property BNRItem *containedItem;

@property BNRItem *container;

@property NSString *itemName;

@property NSString *serialNumber;

@property int valueInDollars;

@property NSDate *dateCreated;

+ (instancetype)randomItem;

- (instancetype)initWithItemName:(NSString *)name

                  valueInDollars:(int)value

                    serialNumber:(NSString *)sNumber;

- (instancetype)initWithItemName:(NSString *)name;

- (void)setItemName:(NSString *)str;

- (NSString *)itemName;

- (void)setSerialNumber:(NSString *)str;

- (NSString *)serialNumber;

- (void)setValueInDollars:(int)v;

- (int)valueInDollars;

- (NSDate *)dateCreated;

- (void)setContainedItem:(BNRItem *)item;

- (BNRItem *)containedItem;

- (void)setContainer:(BNRItem *)item;

- (BNRItem *)container;

@end

Now, BNRItem.h is much easier to read:

@interface BNRItem : NSObject

+ (instancetype)randomItem;

- (instancetype)initWithItemName:(NSString *)name

                  valueInDollars:(int)value

                    serialNumber:(NSString *)sNumber;

- (instancetype)initWithItemName:(NSString *)name;

@property BNRItem *containedItem;

@property BNRItem *container;

@property NSString *itemName;

@property NSString *serialNumber;

@property int valueInDollars;

@property NSDate *dateCreated;

@end

Notice that the names of the properties are the names of the instance variables minus the underscore. The instance variable generated by a property, however, does have an underscore in its name.

Let’s look at an example. When you declared the property named itemName, you got an instance variable named _itemName, a getter method named itemName, and a setter method named setItemName:. (Note that these declarations will not appear in your file; they are declared by the compiler behind the scenes.) Thus, the rest of the code in your application can work as before.

Declaring these properties also takes care of the implementations of the accessors. In BNRItem.m, delete the accessor implementations.

- (void)setItemName:(NSString *)str

{

    _itemName = str;

}

- (NSString *)itemName

{

    return _itemName;

}

- (void)setSerialNumber:(NSString *)str

{

    _serialNumber = str;

}

- (NSString *)serialNumber

{

    return _serialNumber;

}

- (void)setValueInDollars:(int)p

{

    _valueInDollars = p;

}

- (int)valueInDollars

{

    return _valueInDollars;

}

- (NSDate *)dateCreated

{

    return _dateCreated;

}

- (void)setContainedItem:(BNRItem *)item

{

    _containedItem = item;

    // When given an item to contain, the contained

    // item will be given a pointer to its container

    item.container = self;

}

- (BNRItem *)containedItem

{

    return _containedItem;

}

- (void)setContainer:(BNRItem *)item

{

    _container = item;

}

- (BNRItem *)container

{

    return _container;

}

You may be wondering about the implementation of setContainedItem: that you just deleted. This setter did more than just set the _containedItem instance variable. It also set the _container instance variable of the passed-in item. To replicate this functionality, you will shortly write a custom setter for the containedItem property. But first, let’s discuss some property basics.

Property attributes

A property has a number of attributes that allow you to modify the behavior of the accessor methods and the instance variable it creates. The attributes are declared in parentheses after the @property directive. Here is an example:

@property (nonatomic, readwrite, strong) NSString *itemName;

Each attribute has a set of possible values, one of which is the default and does not have to be explicitly declared.

Multi-threading attribute

The multi-threading attribute of a property has two values: nonatomic or atomic. (Multi-threading is outside the scope of this book, but you still need to know the values for this attribute.) Most iOS programmers typically use nonatomic. We do at Big Nerd Ranch, and so does Apple. In this book, you will use nonatomic for all properties.

Unfortunately, the default value for this attribute is atomic, so you have to specify that you want your properties to be nonatomic.

In BNRItem.h, change all of your properties to be nonatomic.

@interface BNRItem : NSObject

+ (instancetype)randomItem;

- (instancetype)initWithItemName:(NSString *)name

        valueInDollars:(int)value

          serialNumber:(NSString *)sNumber;

- (instancetype)initWithItemName:(NSString *)name;

@property (nonatomic) BNRItem *containedItem;

@property (nonatomic) BNRItem *container;

@property (nonatomic) NSString *itemName;

@property (nonatomic) NSString *serialNumber;

@property (nonatomic) int valueInDollars;

@property (nonatomic) NSDate *dateCreated;

@end

Read/write attribute

The read/write attribute’s value, readwrite or readonly, tells the compiler whether to implement a setter method for the property. A readwrite property implements both a setter and getter. A readonly property just implements a getter. The default option for this attribute is readwrite. This is what you want for all of BNRItem’s properties except dateCreated, which should be readonly.

In BNRItem.h, declare dateCreated as a readonly property so that no setter method is generated for this instance variable.

@property (nonatomic, readonly) NSDate *dateCreated;

Memory management attribute

The memory management attribute’s values are strong, weak, copy, and unsafe_unretained. This attribute describes the type of reference that the object with the instance variable has to the object that the variable is pointing to.

For properties that do not point to objects (like the int valueInDollars), there is no need for memory management, and the only option is unsafe_unretained. This is direct assignment. You may also see the value assign in some places, which was the term used before ARC.

(The “unsafe” part of unsafe_unretained is misleading when dealing with non-object properties. It comes from contrasting unsafe unretained references with weak references. Unlike a weak reference, an unsafe unretained reference is not automatically set to nil when the object that it points to is destroyed. This is unsafe because you could end up with dangling pointers. However, the issue of dangling pointers is irrelevant when dealing with non-object properties.)

As the only option, unsafe_unretained is also the default value for non-object properties, so you can leave the valueInDollars property as is.

For properties that manage a pointer to an Objective-C object, all four options are possible. The default is strong. However, Objective-C programmers tend to explicitly declare this attribute. (One reason is that the default value has changed in the last few years, and that could happen again.)

In BNRItem.m, set the memory management attribute as strong for the containedItem and dateCreated properties and weak for the container property.

@property (nonatomic, strong) BNRItem *containedItem;

@property (nonatomic, weak) BNRItem *container;

@property (nonatomic) NSString *itemName;

@property (nonatomic) NSString *serialNumber;

@property (nonatomic) int valueInDollars;

@property (nonatomic, readonly, strong) NSDate *dateCreated;

Setting the container property to weak prevents the strong reference cycle that you caused and fixed earlier.

What about the itemName and serialNumber properties? These point to instances of NSString. When a property points to an instance of a class that has a mutable subclass (like NSString/NSMutableString or NSArray/NSMutableArray), you should set its memory management attribute to copy.

In BNRItem.m, set the memory management attribute for itemName and serialNumber as copy.

@property (nonatomic, strong) BNRItem *containedItem;

@property (nonatomic, weak) BNRItem *container;

@property (nonatomic, copy) NSString *itemName;

@property (nonatomic, copy) NSString *serialNumber;

@property (nonatomic) int valueInDollars;

@property (nonatomic, readonly, strong) NSDate *dateCreated;

Here is what the generated setter for itemName will look like:

- (void)setItemName:(NSString *)itemName

{

    _itemName = [itemName copy];

}

Instead of setting _itemName to point to the incoming object, this setter sends the message copy to that object. The copy method returns an immutable NSString object that has the same values as the original string, and _itemName is set to point at the new string.

Why is it safer to do this for NSString? It is safer to make a copy of the object rather than risk pointing to a possibly mutable object that could have other owners who might change the object without your knowledge.

For instance, imagine if an item was initialized so that its itemName pointed to an NSMutableString.

NSMutableString *mutableString = [[NSMutableString alloc] init];

BNRItem *item = [[BNRItem alloc] initWithItemName:mutableString

                                   valueInDollars:5

                                     serialNumber:@"4F2W7"]];

This code is valid because an instance of NSMutableString is also an instance of its superclass, NSString. The problem is that the string pointed to by mutableString can be changed without the knowledge of the item that also points to it.

In your application, you are not going to change this string unless you mean to. However, when you write classes for others to use, you cannot control how they will be used, and you have to program defensively.

In this case, the defense is to declare itemName with the copy attribute.

In terms of ownership, copy gives you a strong reference to the object pointed to. The original string is not modified in any way: it does not gain or lose an owner, and none of its data changes.

While it is wise to make a copy of an mutable object, it is wasteful to make a copy of an immutable object. An immutable object cannot be changed, so the kind of problem described above cannot occur. To prevent needless copying, immutable classes implement copy to quietly return a pointer to the original and immutable object.

Custom accessors with properties

By default, the accessors that a property implements are very simple and look like this:

- (void)setContainedItem:(BNRItem *)item

{

    _containedItem = item;

}

- (BNRItem *)containedItem

{

    return _containedItem;

}

For most properties, this is exactly what you want. However, for the containedItem property, the default setter method is not sufficient. The implementation of setContainedItem: needs an extra step: it should also set the container property of the item being contained.

You can replace the default setter by implementing the setter yourself in the implementation file.

In BNRItem.m, add back an implementation for setContainedItem:.

- (void)setContainedItem:(BNRItem *)containedItem

{

    _containedItem = containedItem;

    self.containedItem.container = self;

}

When the compiler sees that you have implemented setContainedItem:, it will not create a default setter for containedItem. It will still create the getter method, containedItem.

Note that if you implement both a custom setter and a custom getter (or just a custom getter on a read-only property), then the compiler will not create an instance variable for your property. If you need one, you must declare it yourself.

Note the moral: sometimes the default accessors do not do what you need, and you will need to implement them yourself.

Now you can build and run the application. The leaner BNRItem works in the exact same way.

For the More Curious: Property Synthesis

When explaining properties in this chapter, we noted that a property automatically generates the implementation for the accessor methods and it declares and creates an instance variable. While this is true, we omitted the fact that this behavior is only the default and you have other options.

Declaring a property in a class interface only declares the accessor methods in a class interface. In order for a property to automatically generate an instance variable and the implementations for its methods, it must be synthesized, either implicitly or explicitly. Properties are implicitly synthesized by default. A property is explicitly synthesized by using the @synthesize directive in an implementation file:

@implementation Person

// Generates the code for -setAge: and -age,

// and creates the instance variable _age

@synthesize age = _age;

// Other methods go here

@end

This is how properties are automatically synthesized. The first attribute (age) says “create methods named age and setAge:,” and the second attribute (_age) says “the instance variable that backs these methods should be _age.”

You can optionally leave off the variable name, which creates a backing variable with the same name as the accessors.

@synthesize age;

// Is the same as:

@synthesize age = age;

There are cases where you do not want an instance variable to back a property and therefore do not want a property to automatically generate the accessor method implementations. Consider a Person class with three properties, spouse, lastName, and lastNameOfSpouse:

@interface Person : NSObject

@property (nonatomic, strong) Person *spouse;

@property (nonatomic, copy) NSString *lastName;

@property (nonatomic, copy) NSString *lastNameOfSpouse;

@end

In this somewhat contrived example, it makes sense for both the spouse and lastName properties to be backed by an instance variable. After all, this is information that each Person needs to hang onto. However, it does not make sense to hold onto the last name of the spouse as an instance variable. A Person can just ask their spouse for their lastName, so storing this information in both Person instances is redundant and therefore prone to error. Instead, the Person class would implement the getter and setter for the lastNameOfSpouse property like so:

@implementation Person

- (void)setLastNameOfSpouse:(NSString *)lastNameOfSpouse

{

    self.spouse.lastName = lastNameOfSpouse;

}

- (NSString *)lastNameOfSpouse

{

    return self.spouse.lastName;

}

@end

In this case, because you have implemented both accessors, the compiler will not automatically synthesize an instance variable for lastNameOfSpouse. Which is exactly what you would hope for.

For the More Curious: Autorelease Pool and ARC History

Before automatic reference counting (ARC) was added to Objective-C, we had manual reference counting. With manual reference counting, ownership changes only happened when you sent an explicit message to an object.

[anObject release]; // anObject loses an owner

[anObject retain]; // anObject gains an owner

This was a bummer: Forgetting to send release to an object before setting a pointer to point at something else would create a memory leak. Sending release to an object if you had not previously sent retain to the object was a premature deallocation. A lot of time was spent debugging these problems, which could become very complex in large projects.

During the dark days of manual reference counting, Apple was contributing to an open source project known as the Clang static analyzer and integrating it into Xcode. You will see more about the static analyzer in Chapter 14, but the basic gist is that it could analyze code and tell you if you were doing something silly. Two of the silly things it could detect were memory leaks and premature deallocations. Smart programmers would run their code through the static analyzer to detect these problems and then write the necessary code to fix them.

Eventually, the static analyzer got so good that Apple thought, “Why not just let the static analyzer insert all of the retain and release messages?” Thus, ARC was born. People rejoiced in the streets, and most memory management problems became a thing of the past.

Another thing programmers had to understand in the days of manual reference counting was the autorelease pool. When an object was sent the message autorelease, the autorelease pool would take ownership of an object temporarily so that it could be returned from the method that created it without burdening the creator or the receiver with ownership responsibilities. This was crucial for convenience methods that created a new instance of some object and returned it:

+ (BNRItem *)someItem

{

    BNRItem *item = [[[BNRItem alloc] init] autorelease];

    return item;

}

Because you had to send the release message to an object to relinquish ownership, the caller of this method had to understand its its ownership responsibilities. But it was easy to get confused.

BNRItem *item = [BNRItem someItem]; // I guess I own this now?

NSString *string = [item itemName]; // Well, if I own that, do I own this?

Thus, objects created by methods other than alloc and copy would be sent autorelease before being returned, and the receiver of the object would take ownership as needed or just let it be destroyed when the autorelease pool was drained.

With ARC, this is done automatically (and sometimes optimized out completely). An autorelease pool is created by the @autoreleasepool directive followed by curly braces. Inside those curly braces, any newly instantiated object returned from a method that does not have alloc or copy in its name is placed in that autorelease pool. When the curly brace closes, any object in the pool loses an owner.

@autoreleasepool {

    // Get a BNRItem back from a method that created it,

    // method does not say alloc/copy

    BNRItem *item = [BNRItem someItem];

} // Pool is drained, item loses an owner and is destroyed

iOS applications automatically create an autorelease pool for you, and you really do not have to concern yourself with it. But isn’t it nice to know what that @autoreleasepool is for?