Objective-C Programming_ The Big Nerd Ranch Guide - Aaron Hillegass [42]
- (void)setHolder:(Employee *)e
{
holder = e;
[e addAssetsObject:self];
}
This approach is not at all common.
In the method that adds the child to the parent’s collection, set the child’s pointer.
In this exercise, you will take this last option. In Employee.m, extend the addAssetsObject: method to also set holder:
- (void)addAssetsObject:(Asset *)a
{
// Is assets nil?
if (!assets) {
// Create the array
assets = [[NSMutableArray alloc] init];
}
[assets addObject:a];
[a setHolder:self];
}
(One of my favorite bugs: have both accessors automatically call the other. This creates an infinite loop: addAssetsObject: calls setHolder: which calls addAssetsObject: which calls setHolder: which….)
Build and run the program. You should see something like this:
Employees: (
" " " " " " " " " " ) Giving up ownership of one employee Giving up ownership of array deallocating deallocating deallocating Notice that now none of the employees with assets are getting deallocated properly. Also, none of the assets are being deallocated, either. Why? Retain cycles To find retain cycles in your program, you can use Apple’s profiling tool, Instruments. When you profile a program, you monitor it while it runs to see what’s happening behind the scenes with your code and the system. However, your program runs and exits very, very quickly. To give you time to profile, put in a hundred seconds of sleep() at the end of your main() function: ... } sleep(100); return 0; } In Xcode, choose Product → Profile in the menu. Instruments will launch. When the list of possible profiling instruments appears, choose Leaks: Figure 20.2 Picking a profiler As your program runs, you can browse the state of things. You have two instruments to choose from on the lefthand side of the window (Figure 20.3). Clicking on the Allocations instrument will let you see a bar graph of everything that has been allocated in your heap: Figure 20.3 Allocations instrument You can see, for example, that 10 instances of Asset are still living on your heap. To look for retain cycles, change to the Leaks instrument and choose the Cycles view from the menu bar above the table. Select a particular cycle to see an object graph of it: Figure 20.4 Leaks instrument Weak references #import @class Employee; @interface Asset : NSObject { NSString *label; __weak Employee *holder; unsigned int resaleValue; } @property (strong) NSString *label; @property (weak) Employee *holder; @property unsigned int resaleValue; @end Build and run the program. Note that all the objects are now being deallocated correctly. In a parent-child relationship, the general rule for preventing this type of retain cycle is the parent owns the child, but the child should not own the parent. Zeroing of weak references
The asset owns the employee and the employee owns the assets array, and the assets array owns the asset. It is an island of garbage created by this circle of ownership. These objects should be getting deallocated to free up memory, but they aren’t. This is known as a retain cycle. Retain cycles are a very common source of memory leaks.
How do you fix a retain cycle? Use a weak reference. A weak reference is a pointer that does not imply ownership. To fix our retain cycle, an asset should not own its holder. Edit Asset.h to make holder a weak reference:
To see weak references in action, let’s add another array to the mix. What if we wanted an array of all assets – even ones that have not been assigned to a particular employee? We could add