- •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
Chapter 30 Properties
However, you used the @synthesize construct to implement them:
@synthesize productName, voltage;
Objective-C compiler trivia: When compiling for iOS or a 64-bit Mac OS X program, you don’t need to declare the instance variables. The @property/@synthesize calls are sufficient to make the space for the data.
Open up Appliances and comment out the instance variables in Appliance.h:
{
//NSString *productName;
//int voltage;
}
Rebuild and run the program.
In this book, we always declare the instance variables. It is a nice form of documentation and makes your code usable within 32-bit Mac OS X programs. Uncomment the instance variables.
Property attributes
Now let’s take a closer look at the different attributes you can use to control how the accessors for a property will be created.
Mutability
A property can be declared readwrite or readonly. The default is readwrite, which means that both a setter and a getter method are created. If you don’t want a setter method to be created, you mark the property as readonly:
@property (readonly) int voltage;
Lifetime specifiers
A property can also be declared unsafe_unretained, strong, weak, or copy. This option determines how the setter handles its memory management.
unsafe_unretained is the default and the simplest: it just assigns the passed-in value to the property. Imagine this declaration and definition:
@property (unsafe_unretained) int averageScore;
// "@property int averageScore" would also work here
...
@synthesize averageScore;
This would result in a setter method that’s pretty much equivalent to:
- (void)setAverageScore:(int)d
{
averageScore = d;
}
In Appliance, voltage is an unsafe, unretained property. You will always use unsafe_unretained for properties that hold non-objects.
218
Lifetime specifiers
strong, as you learned in Chapter 20, will ensure that a strong reference is kept to the passed-in object. It will also let go of ownership of the old object (which will then deallocate itself if it has no other owners). For properties that hold objects, you will usually use strong.
weak does not imply ownership of the object pointed to. It will synthesize a setter that sets the property to the passed-in object. If this object is deallocated, the property will be set to nil. (Note that if the pointer is unsafe_unretained and the object it points to is deallocated, you will have a “dangling pointer.” Sending a message to a dangling pointer usually crashes your program.)
copy forms a strong reference to a copy of the object passed in. But, there is a detail in this that most
people misunderstand...
copy
The copy option makes a copy of an object and then changes the pointer to refer to this copy. Imagine you had a property declaration and definition like this:
@property (copy) NSString *lastName; @synthesize lastName;
The generated setter method would look somewhat like this:
- (void)setLastName:(NSString *)d
{
lastName = [d copy];
}
Use of the copy attribute is most common with object types that have mutable subclasses. For example, NSString has a subclass called NSMutableString. You can imagine that your setLastName: method might be passed a mutable string:
// Create a mutable string
NSMutableString *x = [[NSMutableString alloc] initWithString:@"Ono"];
//Pass it to setLastName: [myObj setLastName:x];
//'copy' prevents this from changing the lastName [x appendString:@" Lennon"];
What if the object passed in is not mutable? It seems wasteful to make a copy of an immutable object. The copy method just calls copyWithZone: and passes nil as the argument. For example, in NSString, the copyWithZone: method is overridden to look like this:
- (id)copyWithZone:(NSZone *)z
{
return self;
}
That is, it doesn’t make a copy at all. (Note that NSZone and memory zoning in general are all but deprecated, vestigial features of Cocoa programming, so we won’t go further into them here. copyWithZone: still has some use, however, and has not been entirely phased out.)
For objects that come in mutable and immutable versions, the copy method returns an immutable copy. For example, NSMutableString has a copy method that returns an instance of NSString. If you want the copy to be a mutable object, use the mutableCopy method.
219