Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Objective-C.Programming.pdf
Скачиваний:
14
Добавлен:
21.02.2016
Размер:
8.64 Mб
Скачать

Retain cycles

- (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: (

"<Employee 0: $0 in assets>", "<Employee 1: $153 in assets>", "<Employee 2: $119 in assets>", "<Employee 3: $68 in assets>", "<Employee 4: $0 in assets>", "<Employee 5: $136 in assets>", "<Employee 6: $119 in assets>", "<Employee 7: $34 in assets>", "<Employee 8: $0 in assets>", "<Employee 9: $136 in assets>"

)

Giving up ownership of one employee Giving up ownership of array deallocating <Employee 0: $0 in assets> deallocating <Employee 4: $0 in assets> deallocating <Employee 8: $0 in assets>

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

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.

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:

127

Chapter 20 Preventing Memory 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.

128

Weak references

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

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:

#import <Foundation/Foundation.h> @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.

129

Chapter 20 Preventing Memory Leaks

Zeroing of weak references

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 the assets to an array as they are created. Add a few lines of code to main.m:

#import <Foundation/Foundation.h> #import "Employee.h"

#import "Asset.h"

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

{

@autoreleasepool {

// Create an array of Employee objects

NSMutableArray *employees = [[NSMutableArray alloc] init]; for (int i = 0; i < 10; i++) {

// Create an instance of Employee

Employee *person = [[Employee alloc] init];

//Give the instance variables interesting values [person setWeightInKilos:90 + i];

[person setHeightInMeters:1.8 - i/10.0]; [person setEmployeeID:i];

//Put the employee in the employees array [employees addObject:person];

}

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

// Create 10 assets

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

// Create an asset

Asset *asset = [[Asset alloc] init];

// Give it an interesting label

NSString *currentLabel = [NSString stringWithFormat:@"Laptop %d", i]; [asset setLabel:currentLabel];

[asset setResaleValue:i * 17];

//Get a random number between 0 and 9 inclusive NSUInteger randomIndex = random() % [employees count];

//Find that employee

Employee *randomEmployee = [employees objectAtIndex:randomIndex];

// Assign the asset to the employee [randomEmployee addAssetsObject:asset];

[allAssets addObject:asset];

}

NSLog(@"Employees: %@", employees);

NSLog(@"Giving up ownership of one employee");

130

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]