A: 

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.

Eimantas
That isn't what is happening here.
bbum
A: 

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

Rob
More or less correct. See my answer for specifics.
bbum
+4  A: 

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
bbum
You can also use +(BOOL) accessInstanceVariablesDirectly { return NO; } to eliminate this KVC behavior
sbooth
Good point and a good habit to get into. Thanks!
bbum
Great answer, thanks. The reason I was asking this question was that one of the main points of the chapter is that KVC uses the setters and getters. I'd assumed that was the only way they worked.The init style is the style he uses in the book. He says that the classes he subclasses never return nil, so he leaves out the check for simplicity.
nevan
KVC will use the setter/getter when present, but then it follows up with a rather... surprising... set of heuristics, as you discovered. A bit unfathomable, frankly, unless you know exactly what is going on.
bbum
Meh; the init style in the book is wrong. I'll have to contact Aaron.
bbum