NSTokenField 代表 Core Data 的多对多关系

Posted

技术标签:

【中文标题】NSTokenField 代表 Core Data 的多对多关系【英文标题】:NSTokenField representing Core Data to-many relationship 【发布时间】:2010-10-07 11:00:16 【问题描述】:

我在弄清楚如何在 NSTokenField 中表示多对多关系模型时遇到问题。我有两个(相关)模型:

项目 标记

一个项目可以有很多标签,一个标签可以有很多项目。所以这是一对多的反比关系。

我想做的是在 NSTokenField 中表示这些标签。我想最终得到一个自动建议匹配项的标记字段(找到了一种使用 tokenfield:completionsForSubstring:indexOfToken:indexOfSelectedItem 的方法),并且如果它与现有标记实体不匹配,则能够添加新标记实体。

好的,希望你还在我身边。我正在尝试使用绑定和数组控制器来完成所有这些工作(因为这是最有意义的,对吧?)

我有一个数组控制器“Item Array Controller”,它绑定到我的应用程序委托 managedObjectContext。显示所有项目的 tableview 绑定到此数组控制器。

我的 NSTokenField 的值绑定到数组控制器选择键和模型键路径:标签。

使用此配置,NSTokenField 不会显示标签。它只是给了我:

<NSTokenFieldCell: 0x10014dc60>: Unknown object type assigned (Relationship objects for (
    <NSManagedObject: 0x10059bdc0> (entity: Tag; id: 0x10016d6e0 <x-coredata://9D77D47A-1171-4397-9777-706F599D7E3B/Tag/p102> ; data: <fault>)
) on 0x100169660).  Ignoring...

这对我来说很有意义,所以不用担心。我查看了一些 NSTokenField 委托方法,似乎我应该使用:

- (NSString *)tokenField:(NSTokenField *)tokenField displayStringForRepresentedObject:(id)representedObject

问题是,这个方法没有被调用,我得到了和以前一样的错误。

好的,所以我的下一步是尝试制作一个 ValueTransformer。从带有标签实体的数组转换 -> 带有字符串(标签名称)的数组都很好。另一种方式更具挑战性。

我尝试的是在我的共享应用程序委托托管对象上下文中查找每个名称并返回匹配的标签。这显然给我带来了不同托管对象上下文的问题:

Illegal attempt to establish a relationship 'tags' between objects in different contexts (source = <NSManagedObject: 0x100156900> (entity: Item; id: 0x1003b22b0 <x-coredata://9D77D47A-1171-4397-9777-706F599D7E3B/Item/p106> ; data: 
author = "0x1003b1b30 <x-coredata://9D77D47A-1171-4397-9777-706F599D7E3B/Author/p103>";
createdAt = nil;
filePath = nil;
tags =     (
);
title = "Great presentation";
type = "0x1003b1150 <x-coredata://9D77D47A-1171-4397-9777-706F599D7E3B/Type/p104>";
) , destination = <NSManagedObject: 0x114d08100> (entity: Tag; id: 0x100146b40 <x-coredata://9D77D47A-1171-4397-9777-706F599D7E3B/Tag/p102> ; data: <fault>))

我哪里错了?我该如何解决这个问题?它甚至是正确的方法吗(我觉得你必须使用 ValueTransformer 很奇怪?)

提前致谢!

【问题讨论】:

我今天花了一些时间研究这个 - 仍然无法找到解释这个的资源。我希望有人会在这里救援! :) 【参考方案1】:

我编写了一个自定义NSValueTransformer 来映射绑定NSManagedObject/TagNSSet 和令牌字段的NSStringNSArray。以下是两种方法:

- (id)transformedValue:(id)value 
  if ([value isKindOfClass:[NSSet class]]) 
    NSSet *set = (NSSet *)value;
    NSMutableArray *ary = [NSMutableArray arrayWithCapacity:[set count]];
    for (Tag *tag in [set allObjects]) 
      [ary addObject:tag.name];
    
    return ary;
  
  return nil;


- (id)reverseTransformedValue:(id)value 
  if ([value isKindOfClass:[NSArray class]]) 
    NSArray *ary = (NSArray *)value;
    // Check each NSString in the array representing a Tag name if a corresponding
    // tag managed object already exists
    NSMutableSet *tagSet = [NSMutableSet setWithCapacity:[ary count]];
    for (NSString *tagName in ary) 
      NSManagedObjectContext *context = [[NSApp delegate] managedObjectContext];
      NSFetchRequest *request = [[NSFetchRequest alloc] init];

      NSPredicate *searchFilter = [NSPredicate predicateWithFormat:@"name = %@", tagName];
      NSEntityDescription *entity = [NSEntityDescription entityForName:[Tag className] inManagedObjectContext:context];

      [request setEntity:entity];
      [request setPredicate:searchFilter];

      NSError *error = nil;
      NSArray *results = [context executeFetchRequest:request error:&error];
      if ([results count] > 0) 
        [tagSet addObjectsFromArray:results];
      
      else 
        Tag *tag = [[Tag alloc] initWithEntity:entity insertIntoManagedObjectContext:context];
        tag.name = tagName;

        [tagSet addObject:tag];
        [tag release];
      
    
    return tagSet;
  
  return nil;

CoreData 似乎在返回时自动建立对象关系(但我还没有完全验证这一点)

希望对你有帮助。

【讨论】:

【参考方案2】:

您的第二个错误是由于两个单独的托管对象上下文具有相同的模型并同时处于活动状态。您试图在一个上下文中创建一个对象,然后在第二个上下文中将其关联到另一个对象。这是不允许的。您需要失去第二个上下文,并在一个上下文中建立所有关系。

您的初始错误是由不完整的密钥路径引起的。根据您的描述,听起来您正在尝试使用 ItemsArrayController.selectedItem.tags 填充令牌字段,但这只会返回令牌字段无法使用的 Tag 对象。相反,您需要为其提供转换为字符串的内容,例如ItemsArrayController.selectedItem.tags.name

【讨论】:

【参考方案3】:

2 个问题:

1) 除了您的应用委托的上下文之外,您还使用了 NSManagedObjectContext 吗? 2) 实现 tokenField:displayStringForRepresentedObject: 的对象是否设置为 NSTokenField 的委托?

【讨论】:

以上是关于NSTokenField 代表 Core Data 的多对多关系的主要内容,如果未能解决你的问题,请参考以下文章

使用 Core Data 的主/详细视图

难以调试 iPhone Core Data 错误

Core Data 以编程方式向条目添加属性

CoreData学习:Core Data Stack(Swift)

使用 NSDocument 时如何将情节提要视图绑定到 Core Data 实体?

为不相关的实体集使用和添加多个 Core Data 模型文件