Почему свойство только для чтения по-прежнему позволяет писать с помощью KVC
-
19-09-2019 - |
Вопрос
Я работаю над главой «Кодирование значений ключей» в «Программировании для Mac OS X».Я построил интерфейс со ползунком и меткой, оба привязаны к fido, int.Если я установлю свойство fido только для чтения, перемещение ползунка по-прежнему приведет к изменению значения метки.Я предполагал, что получу за это какую-то ошибку.Если свойство доступно только для чтения, почему ползунок все еще может писать в это свойство?Я думал, что в нем не будут созданы сеттеры и KVC не будет работать.Спасибо.
Вот код, который я использую:
#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
альтернативный текст 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
импортировать «AppController.h»
@implementation AppController
@synthesize fido;
...
@end
На этом этапе вы заявили, что AppController имеет -fido
метод, и вы синтезировали этот метод.Здесь нет -setFido:
метод.Итак, почему следующее «работает»?
- (id)init
{
if (self=[super init]) {
[self setValue:[NSNumber numberWithInt:5] forKey:@"fido"];
NSNumber *n = [self valueForKey:@"fido"];
NSLog(@"fido = %@", n);
}
return self;
}
(КСТАТИ:Я исправил ваш -init для реализации правильного шаблона)
Это работает, потому что KVC следует эвристика чтобы установить или получить значение.Звонок в -setValue:forKey:
сначала ищет -setFoo:
.Если он не найден, он ищет переменную экземпляра. foo
и устанавливает его напрямую.
Обратите внимание: если вы измените переменную экземпляра fido
к _fido
, набор будет работать, но valueForKey
вернет 0, поскольку он вызывает синтезированный метод (поскольку я использую 64-битную версию, @synthesize синтезирует fido
переменная экземпляра.Это сбивает с толку, я знаю.).
Если бы вы изменили имя своего ивара на bar
а затем использовать @synthesize foo=bar;
, код выйдет из строя во время выполнения.
Вот увидишь:
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
Другие советы
Наличие свойства readonly означает, что компилятор не будет генерировать вам метод установки для этого свойства.На него по-прежнему можно писать через KVO/KVC.
Директивы компилятора @property
и @synthesize
— это всего лишь сокращенные способы создания методов для получения и установки рассматриваемой переменной.
Созданный метод установки называется setFido:
, а метод получения просто назван fido
.
Я считаю, что когда вы указываете только чтение, это просто сообщает компилятору не создавать метод установки, а только метод получения.Это не создает никаких препятствий для установки переменной другими способами.
(Надеюсь, я все понял правильно.Удачи!)