- •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
For the More Curious: Manual reference counting and ARC History
[employees removeObjectAtIndex:5];
NSLog(@"allAssets: %@", allAssets);
NSLog(@"Giving up ownership of arrays");
allAssets = nil; employees = nil;
}
sleep(100); return 0;
}
Before you build and run your program, think about what you expect your output to look like. You’ll see the contents of the allAssets array – after Employee #5 has been deallocated. What will the status of Employee #5’s assets be at this point? These assets lose one owner (Employee #5), but they are still owned by allAssets, so they won’t be deallocated.
What about holder for the assets previously owned by Employee #5? When the object that a weak reference points to is deallocated, the pointer variable is zeroed, or set to nil. So Employee #5’s assets will not be deallocated, and their holder variables will be automatically set to nil.
Now build and run the program and check your output:
Employees: (
"<Employee 0: $0 in assets>",
...
"<Employee 9: $136 in assets>"
)
Giving up ownership of one employee deallocating <Employee 5: $136 in assets> allAssets: (
"<Laptop 0: $0, assigned to <Employee 3: $68 in assets>>", "<Laptop 1: $17, assigned to <Employee 6: $119 in assets>>", "<Laptop 2: $34, assigned to <Employee 7: $34 in assets>>",
"<Laptop 3: $51 unassigned>",
"<Laptop 4: $68, assigned to <Employee 3: $68 in assets>>",
"<Laptop 5: $85 unassigned>",
"<Laptop 6: $102, assigned to <Employee 6: $119 in assets>>", "<Laptop 7: $119, assigned to <Employee 2: $119 in assets>>", "<Laptop 8: $136, assigned to <Employee 9: $136 in assets>>", "<Laptop 9: $153, assigned to <Employee 1: $153 in assets>>"
)
Giving up ownership of arrays deallocing <Laptop 3: $51 unassigned>
...
deallocing <Laptop 8: $136 unassigned>
For the More Curious: Manual reference counting and ARC History
As mentioned at the beginning of Chapter 19, before automatic reference counting (ARC) was added to Objective-C, we had manual reference counting, which used retain counts. With manual reference counting, ownership changes only happened when you sent an explicit message to an object that decremented or incremented the retain count.
131
Chapter 20 Preventing Memory Leaks
[anObject release]; // anObject loses an owner [anObject retain]; // anObject gains an owner
You would see these sorts of calls primarily in accessor methods (where the new value was retained and the old value was released) and in dealloc methods (where all the previously retained objects were released). The setHolder: method for Asset would have looked like this:
- (void)setHolder:(Employee *)newEmp
{
[newEmp retain]; [holder release]; holder = newEmp;
}
The dealloc method would have looked like this:
- (void)dealloc
{
[label release]; [holder release]; [super dealloc];
}
What about the description method? It creates and returns a string. Should Asset claim ownership of it? That wouldn’t make sense; the asset is giving away the string it created. When you autorelease an object, you are marking it to be sent release in the future. Before ARC, the description method for Asset would look like this:
- (NSString *)description
{
NSString *result = [[NSString alloc] initWithFormat:@"<%@: $%d >",
[self label], [self resaleValue]];
[result autorelease]; return result;
}
When would it be sent release? When the current autorelease pool was drained:
// Create the autorelease pool
NSAutoreleasePool *arp = [[NSAutoreleasePool alloc] init]; Asset *asset = [[Asset alloc] init];
NSString *d = [asset description];
// The string that d points to is in the autorelease pool
[arp drain]; // The string is sent the message release
ARC uses the autorelease pool automatically, but you must create and drain the pool. When ARC was created, we also got a new syntax for creating an autorelease pool. The code above now looks like this:
// Create the autorelease pool @autoreleasepool {
Asset *asset = [[Asset alloc] init];
NSString *d = [asset description];
//The string that d points to is in the autorelease pool
}// The pool is drained
132
Retain count rules
Retain count rules
There are a set of memory management conventions that all Objective-C programmers follow. If you are using ARC, it is following these conventions behind the scenes for you.
In these rules, I use the word “you” to mean “an instance of whatever class you are currently working on.” It is a useful form of empathy: you imagine that you are the object you are writing. So, for example, “If you retain the string, it will not be deallocated.” really means “If an instance of the class that you are currently working on retains the string, it will not be deallocated.”
Here, then, are the rules. (Implementation details are in parentheses.)
•If you create an object using a method whose name starts with alloc or new or contains copy, then you have taken ownership of it. (That is, assume that the new object has a retain count of 1 and is not in the autorelease pool.) You have a responsibility to release the object when you no longer need it. Here are some of the common methods that convey ownership: alloc (which is always followed by an init method), copy, and mutableCopy.
•An object created through any other means is not owned by you. (That is, assume it has a retain count of one and is already in the autorelease pool, and thus doomed unless it is retained before the autorelease pool is drained.)
•If you don’t own an object and you want to ensure its continued existence, take ownership by sending it the message retain. (This increments the retain count.)
•When you own an object and no longer need it, send it the message release or autorelease. (release decrements the retain count immediately. autorelease causes the message release to be sent when the autorelease pool is drained.)
•As long as an object has at least one owner, it will continue to exist. (When its retain count goes to zero, it is sent the message dealloc.)
One of the tricks to understanding memory management is to think locally. The Asset class does not need to know anything about other objects that also care about its label. As long as an Asset instance retains objects it wants to keep, you won’t have any problems. Programmers new to the language sometimes make the mistake of trying to keep tabs on objects throughout an application. Don’t do this. If you follow these rules and always think local to a class, you never have to worry what the rest of an application is doing with an object.
Following the idea of ownership, now it becomes clear why you need to autorelease the string in your description method: The employee object created the string, but it doesn’t want to own it. It wants to give it away.
133
This page intentionally left blank