I'm working through the "Key Value Coding" chapter in "Programming for Mac OS X". I've built an interface with a slider and a label, both bound to fido, an int. If I set the property for fido to readonly, moving the slider still causes the label to change it's value. I had assumed that I'd get some sort of error for this. If the property is readonly, how come the slider can still write to the property? I thought that it would have no setters created, and KVC wouldn't work. Thanks.
Here's the code I'm using:
#import <Cocoa/Cocoa.h>
@interface AppController : NSObject
{
int fido;
}
@property (readonly, assign) int fido;
@end
#import "AppController.h"
@implementation AppController
@synthesize fido;
- (id)init
{
[super init];
[self setValue:[NSNumber numberWithInt:5] forKey:@"fido"];
NSNumber *n = [self valueForKey:@"fido"];
NSLog(@"fido = %@", n);
return self;
}
@end
alt text http://idisk.me.com/nevan/Public/Pictures/Skitch/Window-20091001-174352.png
AppController.h:
@interface AppController : NSObject
{
int fido;
}
@property (readonly, assign) int fido;
@end
@implementation AppController
@synthesize fido;
...
@end
At this point, you have declared that AppController has a -fido
method and you have synthesized that method. There is no -setFido:
method. So, why does the following "work"?
- (id)init
{
if (self=[super init]) {
[self setValue:[NSNumber numberWithInt:5] forKey:@"fido"];
NSNumber *n = [self valueForKey:@"fido"];
NSLog(@"fido = %@", n);
}
return self;
}
(BTW: I fixed your -init to implement the correct pattern)
This works because KVC follows a heuristic to set or get the value. The call to -setValue:forKey:
first looks for -setFoo:
. If not found, it then looks for the instance variable foo
and sets it directly.
Note that if you change the instance variable fido
to _fido
, the set will work, but the valueForKey
will return 0 as it calls the synthesized method (since I'm on 64 bit, the @synthesize synthesizes a fido
instance variable. Confusing, I know.).
If you were to change the name of your ivar to bar
and then use @synthesize foo=bar;
, the code would fail at runtime.
You'll see:
2009-10-01 08:59:58.081 dfkjdfkjfjkfd[24099:903] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<AppController 0x20000e700> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key fido.'
*** Call stack at first throw:
(
0 CoreFoundation 0x00007fff85b055a4 __exceptionPreprocess + 180
1 libobjc.A.dylib 0x00007fff85c5a0f3 objc_exception_throw + 45
2 CoreFoundation 0x00007fff85b5caf9 -[NSException raise] + 9
3 Foundation 0x00007fff814e14f5 -[NSObject(NSKeyValueCoding) setValue:forKey:] + 434
(
0 CoreFoundation 0x00007fff85b055a4 __exceptionPreprocess + 180
1 libobjc.A.dylib 0x00007fff85c5a0f3 objc_exception_throw + 45
2 CoreFoundation 0x00007fff85b5caf9 -[NSException raise] + 9
3 Foundation 0x00007fff814e14f5 -[NSObject(NSKeyValueCoding) setValue:forKey:] + 434
4 dfkjdfkjfjkfd 0x0000000100000d96 -[AppController init] + 130
Having readonly property means that compiler won't generate you setter for that property. It's still legal to write to it via KVO/KVC.
The compiler directives @property
and @synthesize
are just shorthand ways to create the methods to get and set the variable in question.
The setter method created is named setFido:
, and the getter method is just named fido
.
When you specify readonly, I believe that simply tells the compiler not to create the setter method, but only the getter. It doesn't put any sort of barrier in the way of setting the variable by other means.
(Hope I've got all that right. Good luck!)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With