Why does a readonly property still allow writing with KVC

getter, objective-c, properties, setter

Solution

AppController.h:

@interface AppController : NSObject
{
        int fido;
}

@property (readonly, assign) int fido;
@end

import "AppController.h"

@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

Problem

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

Original source