Objective-C Programming_ The Big Nerd Ranch Guide - Aaron Hillegass [74]
{
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.
There is no property lifetime specifier called mutableCopy. If you wish for your setter to set the property to be a mutable copy of an object, you must implement the setter yourself so that it calls the mutableCopy method on the incoming object. For example, in OwnedAppliance, you might create a setOwnerNames: method:
- (void)setOwnerNames:(NSSet *)newNames
{
ownerNames = [newNames mutableCopy];
}
More about copying
Most Objective-C classes have no copyWithZone: method at all. Objective-C programmers make fewer copies than you might think.
Curiously, the copy and mutableCopy methods are defined in NSObject like this:
- (id)copy
{
return [self copyWithZone:NULL];
}
- (id)mutableCopy
{
return [self mutableCopyWithZone:NULL];
}
Thus, if you have some code like this:
Appliance *b = [[Appliance alloc] init];
Appliance *c = [b copy];
You will get an error like this:
-[Appliance copyWithZone:]: unrecognized selector sent to instance 0x100110130
Advice on atomic vs. nonatomic
This is an introductory book on programming, and the atomic/nonatomic option relates to a relatively advanced topic known as multithreading. Here is what you need to know: the nonatomic option will make your setter method run a tiny bit faster. If you look at the headers for Apple’s UIKit, every property is marked as nonatomic. You should make your properties nonatomic, too.
(I give this advice to everyone. In every group, however, there is someone who knows just enough to be a pain. That person says, “But when I make my app multithreaded, I’ll need the protection that atomic setter methods get me.” And I should say, “I don’t think you will write multithreaded code anytime soon. And when you do, I don’t think atomic setter methods are going to help.” But what I really say is “OK, then you should leave your setters atomic.” Because you can’t tell someone something they aren’t ready to hear.)
In Appliance.h, make your accessors non-atomic:
@property (copy, nonatomic) NSString *productName;
@property (nonatomic) int voltage;
Sadly, at this time, the default for properties is atomic, so you do have to make this change.
Key-value coding
Key-value coding is the ability to read and set a property using its name. The key-value coding methods are defined in NSObject, and thus every object has this capability.
Open main.m and find the line:
[a setProductName:@"Washing Machine"];
Rewrite the same line to use key-value coding:
[a setValue:@"Washing Machine" forKey:@"productName"];
In this case, the setValue:forKey: method, as defined in NSObject, will go looking for a setter method named setProductName:. If the object doesn’t have a setProductName: method, it will access the instance variable directly.
You can also read the value of a variable using key-value coding. Add a line to main.m that prints out the product name:
int main (int argc, const char * argv[])
{
@autorelease {
Appliance *a = [[Appliance alloc] init];
NSLog(@"a is %@", a);
[a setValue:@"Washing Machine" forKey:@"productName"];
[a setVoltage:240];
NSLog(@"a is %@", a);
NSLog(@"the product name is %@", [a valueForKey:@"productName"]);
}
return 0;
}
In this case, the valueForKey: method, as defined in NSObject, goes looking for an accessor named productName. If there is no productName method, the instance variable is accessed directly.
If you type the name of the property wrong, you won’t get warning from the compiler, but there will be a runtime