-primitiveValueForKey: 有什么意义?

发布于 2024-10-01 14:02:22 字数 251 浏览 15 评论 0原文

-setPrimitiveValue:forKey: 不会触发 KVO 通知。但在我的大脑中,KVO 只有当事情发生变化时才有意义。但是,当我只访问它以进行读取时,如何更改某些内容呢?

-primitiveValueForKey: 仅获取某个键的对象。但它不会修改它。那么为什么在使用 -valueForKey: 时会导致 KVO 通知呢?

(当然有一点,但我还没有看到。)

-setPrimitiveValue:forKey: won't trigger KVO notifications. But in my brain, KVO only makes sense when something changes. But how can change something when I only access it for read?

-primitiveValueForKey: only gets the object for some key. But it won't modify it. So why would/could this cause KVO notifications when using -valueForKey:?

(Of course there is a point, but I don't see it yet.)

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

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

发布评论

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

评论(1

飞烟轻若梦 2024-10-08 14:02:22

-primitiveValueForKey:-setPrimitiveValue:forKey: 方法主要由 Core Data 使用。特别是,核心数据不仅需要知道您何时要修改属性;还需要知道您何时要修改属性。它也需要知道您何时要访问它,以便它可以实现故障排除。

因此,Core Data 添加了在 getter 中使用的 -{will,did}AccessValueForKey: 方法,就像在 setter 中使用的 -{will,did}ChangeValueForKey: 方法一样作为 KVO 钩子。

然而,还有另一个问题:核心数据实际上还为您管理建模属性的底层存储。因此,您需要某种方法来在 -{will,did}{Access,Change}ValueForKey: 方法建立的屏障内操作此底层存储。这就是 -primitiveValueForKey:-setPrimitiveValue:forKey: 发挥作用的地方。

这就是为什么在 存在之前实现核心数据 getter 和 setter 的标准模式@property@dynamic,看起来像这样:

// Person.m
#import "Person.h"

@implementation Person

- (NSString *)name {
    [self willAccessValueForKey:@"name"];
    NSString *value = [self primitiveValueForKey:@"name"];
    [self didAccessValueForKey:@"name"];
}

- (void)setName:(NSString *)value {
    [self willChangeValueForKey:@"name"];
    [self setPrimitiveValue:value forKey:@"name"];
    [self didChangeValueForKey:@"name"];
}

@end

当然,现在,如果您希望 Core Data在运行时为您生成这些内容:

// Person.h
@interface Person : NSManagedObject
@property (nonatomic, readwrite, copy) NSString *name;
@end

// Person.m
#import "Person.h"

@implementation Person
@dynamic name;
@end

但是,在某些情况下,您仍然希望在 KVO 或故障触发的情况下操作底层存储。因此,Core Data 也提供了一种新的方法来实现这一点,也是围绕属性声明和自动合成构建的:

// Person.h
@interface Person : NSManagedObject
@property (nonatomic, readwrite, copy) NSString *name;
@end

// Person.m
#import "Person.h"

@interface Person ()
@property (nonatomic, readwrite, copy) NSString *primitiveName;
@end

@implementation Person
@dynamic name;
@dynamic primitiveName;
@end

请注意,我将类延续放在 .m 文件中;它不是 Person 外部的代码(甚至是 -awakeFromInsert-awakeFromFetch 之外的代码)应该接触的东西。但它确实让我可以获取“name”属性的底层存储,而无需在代码中嵌入文字字符串,并且可以使用真实类型。

The -primitiveValueForKey: and -setPrimitiveValue:forKey: methods are primarily used by Core Data. In particular, Core Data doesn't just need to know when you're going to modify an attribute; it needs to know when you're going to access it as well, so it can implement faulting.

Thus Core Data adds -{will,did}AccessValueForKey: methods to use in getters, just as -{will,did}ChangeValueForKey: methods exist for use in setters to act as KVO hooks.

However, there's another wrinkle: Core Data actually manages the underlying storage of modeled properties for you as well. So you need some way to manipulate this underlying storage within the barriers established by the -{will,did}{Access,Change}ValueForKey: methods. That's where -primitiveValueForKey: and -setPrimitiveValue:forKey: come in.

This is why the standard pattern for implementing Core Data getters and setters, prior to the existence of @property and @dynamic, looked like this:

// Person.m
#import "Person.h"

@implementation Person

- (NSString *)name {
    [self willAccessValueForKey:@"name"];
    NSString *value = [self primitiveValueForKey:@"name"];
    [self didAccessValueForKey:@"name"];
}

- (void)setName:(NSString *)value {
    [self willChangeValueForKey:@"name"];
    [self setPrimitiveValue:value forKey:@"name"];
    [self didChangeValueForKey:@"name"];
}

@end

Now of course, you can just declare a property and define it as @dynamic if you want Core Data to generate this stuff for you at runtime:

// Person.h
@interface Person : NSManagedObject
@property (nonatomic, readwrite, copy) NSString *name;
@end

// Person.m
#import "Person.h"

@implementation Person
@dynamic name;
@end

There are some situations though where you still want to manipulate the underlying storage without KVO or fault-firing, however. Thus Core Data provides a new way to get at this as well, also built around property declarations and automatic synthesis:

// Person.h
@interface Person : NSManagedObject
@property (nonatomic, readwrite, copy) NSString *name;
@end

// Person.m
#import "Person.h"

@interface Person ()
@property (nonatomic, readwrite, copy) NSString *primitiveName;
@end

@implementation Person
@dynamic name;
@dynamic primitiveName;
@end

Note that I put the class continuation in the .m file; it's not something that code outside Person (or even really code outside -awakeFromInsert and -awakeFromFetch) should touch. But it does let me get at the underlying storage for the "name" property without embedding literal strings in my code, and with the use of the real types.

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