为什么只读属性仍然允许使用 KVC 写入

发布于 2024-08-06 05:55:46 字数 805 浏览 6 评论 0原文

我正在学习“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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

山人契 2024-08-13 05:55:46

AppController.h:

@interface AppController : NSObject
{
        int fido;
}

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

import "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 实例变量。我知道这很令人困惑。)。

如果您将 ivar 的名称更改为 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

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
抱着落日 2024-08-13 05:55:46

具有只读属性意味着编译器不会为该属性生成设置器。通过 KVO/KVC 写入它仍然是合法的。

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.

稚然 2024-08-13 05:55:46

编译器指令 @property@synthesize 只是创建获取和设置相关变量的方法的简写方式。

创建的 setter 方法名为 setFido:,getter 方法名为 fido

当您指定 readonly 时,我相信只是告诉编译器不要创建 setter 方法,而只创建 getter 方法。它不会对通过其他方式设置变量的方式设置任何障碍。

(希望我做对了。祝你好运!)

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文