Cocoa NSOutlineView、NSTreeController和异构层次结构
在Marcus Zarra的核心数据手册的第40页中,他建议,由于NSTreeController需要相同的键来访问层次结构中的所有对象(例如,子对象),这可能意味着不太有意义的关系名称,因此可以为所需的关系编写额外的访问器。我认为这是一个好主意,但我不确定如何实施 让我以Aperture的数据模型为例:可以有许多库,每个库可以有许多项目,每个库可以有许多照片。所以,如果我分别命名实体库、项目和照片以及它们之间的关系projects、photos和nothing,那么以下是库的正确实现吗 图书馆Cocoa NSOutlineView、NSTreeController和异构层次结构,cocoa,core-data,cocoa-bindings,Cocoa,Core Data,Cocoa Bindings,在Marcus Zarra的核心数据手册的第40页中,他建议,由于NSTreeController需要相同的键来访问层次结构中的所有对象(例如,子对象),这可能意味着不太有意义的关系名称,因此可以为所需的关系编写额外的访问器。我认为这是一个好主意,但我不确定如何实施 让我以Aperture的数据模型为例:可以有许多库,每个库可以有许多项目,每个库可以有许多照片。所以,如果我分别命名实体库、项目和照片以及它们之间的关系projects、photos和nothing,那么以下是库的正确实现吗 图书馆
@interface Library: NSManagedObject {
}
@property (nonatomic, retain) NSString *title;
@property (nonatomic, retain) NSSet *projects;
@property (nonatomic, retain) NSSet *children;
@property (nonatomic, retain) id parent;
@end
@interface Library (CoreDataGeneratedAccessors)
- (void)addProjectsObject:(Project *)value;
- (void)removeProjectsObject:(Project *)value;
- (void)addProjects:(NSSet *)value;
- (void)removeProjects:(NSSet *)value;
- (id)parent;
- (void)setParent;
@end
和图书馆
#include "Library.h"
@implementation Library
@dynamic title;
@dynamic projects;
- (NSSet*) children {
[self willAccessValueForKey:@"children"];
NSSet *set = [self valueForKey:@"projects"];
[self didAccessValueForKey:@"children"];
return set;
}
- (void) setChildren:(NSSet*)children {
[self willChangeValueForKey:@"children"];
[self setValue:children forKey:@"projects"];
[self didChangeValueForKey:@"children"];
}
- (id)parent {
[self willAccessValueForKey:@"parent"];
[self didAccessValueForKey:@"parent"];
return nil;
}
- (void)setParent:(id)parent {
// Proposed parent value is ignored. Libraries have no parent.
[self willChangeValueForKey:@"parent"];
[self didChangeValueForKey:@"parent"];
}
@end
addChildrenObject:
,removeChildrenObject:
,addChildren:
,和removeChildren:
?并实施它们?(父方法也是如此。)Jorge我认为当前版本的NSTreeController不需要实际命名为“children”和“parent”的属性。您可以使用
setChildrenKeyPath:
将子路径设置为任何属性名称(只要该属性实现父子层次结构)。我很确定这是一个相当古老的要求。(恕我直言,我已经有一段时间没有使用树控制器了。见下文)
至于你的其他问题:
(1) 对
(2) 对,对,对
(3) 关系保存在真实属性中,例如属性。实体/对象图与父子属性虚拟化的虚拟图是分开的。由于父属性和子属性实际上没有值,因此实际值中的任何更改都会立即反映在它们的返回中,反之亦然。简言之,你不必担心他们
(4) 是的,树控制器将观察虚拟属性,而不是真实属性。如果虚拟属性不符合KVO,则控制器将无法工作
(5) 从历史上看,该控制器一直被认为是有缺陷的。自IIRC 2004年以来,它一直存在,但从未真正发挥过作用。很多老手完全忽视了这一点。我有一段时间没用了
(6) 您通常只缓存预期不会有太大变化的数据。如果取数用于实际更新模型,或者需要其他东西来更新模型,例如URL请求,则不应使用缓存 我在这里看到的问题是,虽然设置“children”属性将触发“projects”属性的KVO,但情况并非如此。因此,如果通过“项目”关系将项目添加到库对象中,大纲视图将不会更新,因为它不会看到“子对象”属性中的任何更改 实现这一点的最简单方法是实现如下方法:
+ (NSSet*)keyPathsForValuesAffectingChildren
{
return [NSSet setWithObject:@"projects"];
}
这将使对“projects”属性的任何更改也会触发对“children”的KVO通知
另一方面,由于“children”属性不是核心数据模型的一部分,我认为
will/didAccessValueForKey:
调用是绝对必要的,尽管我不认为它们会损害任何东西。另外,如果您实现了我上面提到的方法,您不需要再调用setChildren:
方法中的will/didChangeValueForKey:
,因为当“projects”键更改时,Cocoa会自动触发KVO。非常感谢您的回答。我的意思不是说它们必须命名为children,而是层次结构中所有对象的关系名称必须相同,而我更喜欢为我的关系命名有意义的名称。另外,提到6),我不认为顶层会经常改变,但当它改变时,我应该怎么做?这更有意义。至于(6)我犯了一个错误,当时正在考虑iOS的NSFetchedResults控制器。对于正常的获取,您必须重新运行获取以更新返回的数组。很抱歉,在多次尝试之后,一定是出了问题。创建项目时,NSOutlineView无法更新接口。如果我退出应用程序(从而保存数据)并再次运行它,项目就在那里。如果我用“正确命名”的关系重新创建模型,而不是使用虚拟访问器,那么它在不更改绑定的情况下运行良好。更多帮助?如果树控制器正在观察虚拟属性,但您正在其他地方更改具体属性,那么控制器将不会收到KVO通知。尝试为一个具体属性添加自定义访问器,该属性调用其中一个虚拟属性的didChangeValueForKey:
,看看这是否会清除它。您是对的,问题是KVO通知。然而,必须为实际属性编写自定义访问器(和方法),这并不完全有效,并且给代码增加了很多复杂性。我更喜欢依赖法。无论如何,谢谢你。是的!这就是诀窍。非常感谢。