不久前我开始使用瞬态属性,并认为我对它们非常了解,但是这个并没有像我想象的那样表现。它在数据模型中被定义为具有未定义类型的瞬态属性,因此被声明为属性:
@property (nonatomic, readonly) NSSet * manufacturers;
该属性被声明为依赖于该对象上的另一个 1:M 关系:
+ (NSSet *) keyPathsForValuesAffectingManufacturers
{
return [NSSet setWithObject:@"lines"];
}
我已经为这个瞬态属性实现了一个相当标准的 getter:
- (NSSet *) manufacturers
{
[self willAccessValueForKey:@"manufacturers"];
NSSet *mfrs = [self primitiveManufacturers];
[self didAccessValueForKey:@"manufacturers"];
if (mfrs == nil)
{
NSMutableSet *working = [NSMutableSet set];
/* rebuild the set of manufacturers into 'working' here */
mfrs = working;
[self setPrimitiveManufacturers:mfrs];
}
return mfrs;
}
不起作用的部分是,manufacturers
当从该对象的关系中添加/删除对象时,缓存的原始值似乎没有被清除lines
。当manufacturers
您添加/删除行时肯定会调用访问器,但[self primitiveManufacturers]
仍会返回旧的缓存值(这显然是非零),因此不会调用重建当前制造商集的代码,我最终会得到一个输出-过时的返回值。
我是否误解了缓存的瞬态属性值应该如何工作?或者我是否应该在通过某种 KVO 回调更改行时手动取消缓存值,以便下次manufacturers
访问“if”中的更新代码?
非常感谢任何帮助。
编辑:我知道我基本上是在模拟与此瞬态属性的 1:M 关系(即瞬态关系,因为需要更好的词),而且我也知道文档警告不要使用setPrimitiveValue:forKey:
for 关系:
您通常应该只使用此方法来修改属性(通常是瞬态的),而不是关系。如果您尝试为新的 NSMutableSet 对象设置一对多关系,它将(最终)失败。在异常情况下需要使用此方法修改关系,首先使用primitiveValueForKey获取现有集合:(确保该方法不返回nil),创建可变副本,然后修改副本
但是,由于我没有在真正的1:M 关系上设置原始值,只是我模拟的瞬态关系,我假设这个警告不适用于我的用例。
编辑#2:因为我只是试图跟踪何时从集合中添加或删除对象lines
(这是预期值manufacturers
可以改变的唯一方法),而不是试图跟踪对已经存在的对象的属性的更新lines
集合,我认为我不需要像这样重量级的东西:https ://github.com/mbrugger/CoreDataDependentProperties
解决方案:根据下面的答案,我实施了以下解决方案以在更新manufacturers
时将集合清空。lines
任何人都可以看到这方面的任何问题,或者提出任何更有效的实施方式吗?重要的是,此操作要快,因为它是在主线程中完成的,并且会明显延迟在line
更新完成的同时发生的 UI 更新。
- (void) awakeFromFetch
{
[super awakeFromFetch];
[self addObserver:self forKeyPath:@"lines" options:0 context:nil];
}
- (void) awakeFromInsert
{
[super awakeFromInsert];
[self addObserver:self forKeyPath:@"lines" options:0 context:nil];
}
- (void) didTurnIntoFault
{
[self removeObserver:self forKeyPath:@"lines"];
[super didTurnIntoFault];
}
- (void) observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
if ([object isEqual:self] && [keyPath isEqualToString:@"lines"])
{
[self setPrimitiveManufacturer:nil];
}
else
{
[super observeValueForKeyPath:keyPath
ofObject:object
change:change
context:context];
}
}