- •C and Objective-C
- •How this book works
- •How the life of a programmer works
- •Installing Apple’s developer tools
- •Getting started with Xcode
- •Where do I start writing code?
- •How do I run my program?
- •So what is a program?
- •Don’t stop
- •Types
- •A program with variables
- •Challenge
- •Boolean variables
- •When should I use a function?
- •How do I write and use a function?
- •How functions work together
- •Local variables, frames, and the stack
- •Recursion
- •Looking at the frames in the debugger
- •return
- •Global and static variables
- •Challenge
- •printf()
- •Integer operations
- •Integer division
- •Operator shorthand
- •Floating-point numbers
- •Tokens for displaying floating-point numbers
- •The while loop
- •The for loop
- •break
- •continue
- •The do-while loop
- •Challenge
- •Getting addresses
- •Storing addresses in pointers
- •Getting the data at an address
- •How many bytes?
- •NULL
- •Stylish pointer declarations
- •Challenges
- •Writing pass-by-reference functions
- •Avoid dereferencing NULL
- •Creating and using your first object
- •Message anatomy
- •Objects in memory
- •Challenge
- •Nesting message sends
- •Multiple arguments
- •Sending messages to nil
- •Challenge
- •Challenge
- •NSMutableArray
- •Reference pages
- •Quick Help
- •Other options and resources
- •Accessor methods
- •Dot notation
- •Properties
- •self
- •Multiple files
- •Challenge
- •Overriding methods
- •super
- •Challenge
- •Object ownership and ARC
- •Creating the Asset class
- •Adding a to-many relationship to Employee
- •Challenge
- •Retain cycles
- •Weak references
- •Zeroing of weak references
- •For the More Curious: Manual reference counting and ARC History
- •Retain count rules
- •NSArray/NSMutableArray
- •Immutable objects
- •Sorting
- •Filtering
- •NSSet/NSMutableSet
- •NSDictionary/NSMutableDictionary
- •Preprocessor directives
- •#include and #import
- •#define
- •Global variables
- •enum
- •#define vs global variables
- •Writing an NSString to a file
- •Reading files with NSString
- •Writing an NSData object to a file
- •Reading an NSData from a file
- •Target-action
- •Helper objects
- •Notifications
- •Which to use?
- •Callbacks and object ownership
- •Challenge
- •Getting started with iTahDoodle
- •BNRAppDelegate
- •Adding a C helper function
- •Objects in iTahDoodle
- •Model-View-Controller
- •The application delegate
- •Setting up views
- •Running on the iOS simulator
- •Wiring up the table view
- •Adding new tasks
- •Saving task data
- •For the More Curious: What about main()?
- •Edit BNRDocument.h
- •A look at Interface Builder
- •Edit BNRDocument.xib
- •Making connections
- •Revisiting MVC
- •Edit BNRDocument.m
- •Writing init methods
- •A basic init method
- •Using accessors
- •init methods that take arguments
- •Deadly init methods
- •Property attributes
- •Mutability
- •Lifetime specifiers
- •copy
- •More about copying
- •Advice on atomic vs. nonatomic
- •Key-value coding
- •Non-object types
- •Defining blocks
- •Using blocks
- •Declaring a block variable
- •Assigning a block
- •Passing in a block
- •typedef
- •Return values
- •Memory management
- •The block-based future
- •Challenges
- •Anonymous block
- •NSNotificationCenter
- •Bitwise-OR
- •Bitwise-AND
- •Other bitwise operators
- •Exclusive OR
- •Complement
- •Left-shift
- •Right-shift
- •Using enum to define bit masks
- •More bytes
- •Challenge
- •char
- •char *
- •String literals
- •Converting to and from NSString
- •Next Steps
- •Index
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