Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does a readonly property still allow writing with KVC

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

like image 924
nevan king Avatar asked Oct 01 '09 08:10

nevan king


3 Answers

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
like image 162
bbum Avatar answered Nov 15 '22 05:11

bbum


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.

like image 28
Eimantas Avatar answered Nov 15 '22 05:11

Eimantas


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!)

like image 37
Rob Avatar answered Nov 15 '22 07:11

Rob