NSOutlineView,NSTreeController和异构层次结构

时间:2010-06-20 15:47:29

标签: cocoa core-data cocoa-bindings

在Marcus Zarra的核心数据手册的第40页中,他建议,由于NSTreeController需要相同的密钥来访问层次结构中的所有对象(例如,子级),这可能意味着关系名称不太有意义,您可以编写额外的适合所需关系的访问者。我认为这是一个好主意,但我不确定如何实现它。

让我以Aperture的数据模型为例:您可以拥有多个库,每个库都可以有很多项目,每个库都可以有很多照片。所以,如果我将我的实体命名为图书馆项目图片及其关系项目照片并且没有分别是以下适当的库实现?

Library.h

@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

和Library.m

#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
  1. 子元素和父元素应该是头文件中的属性吗?

  2. 这是建议的实施吗?我还应该加入addChildrenObject:removeChildrenObject:addChildren:removeChildren:吗?并实施它们? (父方法也是如此。)

  3. 我假设Core Data模型中根本没有出现儿童。是对的吗?那么反推的关系如何推断呢?

  4. 我应该在setChildren中调用[self willChangeValueForKey:@“children”]:所以孩子们是否符合KVO标准? (与其他访问者相同。)

  5. 在第41页中,M。Zarra建议实施NSOutlineDataSource而不是使用NSTreeController,因为“结果可能是意外且不清楚”。有谁知道这些限制是什么?

  6. 最后,如果我实现NSOutlineDataSource,你会建议缓存根对象的获取吗?如果是这样,维护此缓存数组与Core Data同步的正确方法是什么?

  7. 谢谢。

    致以最诚挚的问候,

    豪尔赫

2 个答案:

答案 0 :(得分:1)

我认为当前版本的NSTreeController不需要实际命名为“children”和“parent”的属性。您可以使用setChildrenKeyPath:将子路径设置为任何属性名称(只要该属性实现父子层次结构。)我很确定这是一个相当古老的要求。 (带上一粒盐,我有时没用树控制器。见下文)

至于你的其他问题:

(1)是

(2)是,是和是

(3)关系保持在真实属性中,例如属性。实体/对象图与父子属性虚拟化的虚拟图分开。由于父元素和子元素实际上没有值,因此实际值的任何更改都会立即反映在它们的返回值中,反之亦然。简而言之,您不必担心它们。

(4)是的,树控制器将观察虚拟属性而不是真实属性。如果虚拟属性不符合KVO,则控制器将无法工作。

(5)历史上,NSTreeController被认为是错误的。它自2004年IIRC以来一直存在,它从未真正起作用。许多旧手完全忽略它。我有一段时间没用过它。

(6)您通常只缓存预计不会发生太大变化的数据。如果获取用于实际更新模型或预期其他东西更新模型,例如一个URL请求,那么你不应该使用缓存。

答案 1 :(得分:1)

我在这里看到的问题是,虽然设置“children”属性会触发“projects”属性的KVO,反之则不然。因此,如果通过“项目”关系将项目添加到库对象,则大纲视图将不会更新,因为它不会在“children”属性中看到任何更改。

启用此功能的最简单方法是实现如下方法:

+ (NSSet*)keyPathsForValuesAffectingChildren
{
    return [NSSet setWithObject:@"projects"];
}

那应该对“projects”属性进行任何更改也会触发“孩子”的KVO通知。

另一方面,由于“children”属性不属于您的Core Data模型,我认为will/didAccessValueForKey:调用不是必需的,尽管我认为它们不会受到伤害任何东西。此外,如果您实现上面提到的方法,则不再需要在will/didChangeValueForKey:方法中调用setChildren:,因为当“项目”键更改时,Cocoa应自动触发KVO。