Home > Articles > Programming > General Programming/Other Languages

  • Print
  • + Share This
This chapter is from the book

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

Figure 3.4 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

Figure 3.5 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

Figure 3.6 RandomItems with strong reference cycle avoided

  • + Share This
  • 🔖 Save To Your Account