cocoa - NSOrderedSet生成访问器引发的异常。

  显示原文与译文双语对照的内容

在我的Lion 应用程序中,我有这个数据模型:

enter image description here

内部的关系 subitemsItem是有序

Xcode 4.1 ( 生成 4 B110 ) 为我创建了文件 Item.hItem.mSubItem.hSubItem.h

以下是( 自动生成)的内容:


#import <Foundation/Foundation.h>

#import <CoreData/CoreData.h>

@class SubItem;

@interface Item : NSManagedObject {
@private
}

@property (nonatomic, retain) NSString * name;
@property (nonatomic, retain) NSOrderedSet *subitems;
@end

@interface Item (CoreDataGeneratedAccessors)

- (void)insertObject:(SubItem *)value inSubitemsAtIndex:(NSUInteger)idx;
- (void)removeObjectFromSubitemsAtIndex:(NSUInteger)idx;
- (void)insertSubitems:(NSArray *)value atIndexes:(NSIndexSet *)indexes;
- (void)removeSubitemsAtIndexes:(NSIndexSet *)indexes;
- (void)replaceObjectInSubitemsAtIndex:(NSUInteger)idx withObject:(SubItem *)value;
- (void)replaceSubitemsAtIndexes:(NSIndexSet *)indexes withSubitems:(NSArray *)values;
- (void)addSubitemsObject:(SubItem *)value;
- (void)removeSubitemsObject:(SubItem *)value;
- (void)addSubitems:(NSOrderedSet *)values;
- (void)removeSubitems:(NSOrderedSet *)values;

@end

下面是( 自动生成)的内容:


#import"Item.h"
#import"SubItem.h"

@implementation Item

@dynamic name;
@dynamic subitems;

@end

可以看到,类 Item 提供了一个名为 addSubitemsObject:的方法。 不幸的是,当尝试以这种方式使用它时:


Item *item = [NSEntityDescription insertNewObjectForEntityForName:@"Item" inManagedObjectContext:self.managedObjectContext];
item.name = @"FirstItem";

SubItem *subItem = [NSEntityDescription insertNewObjectForEntityForName:@"SubItem" inManagedObjectContext:self.managedObjectContext];

[item addSubitemsObject:subItem];

出现这里错误:


2011-09-12 10:28:45.236 Test[2002:707] *** -[NSSet intersectsSet:]: set argument is not an NSSet

你能帮我?

时间:

我在你的数据模型和我自己的一个不同的名称中复制了你的安装程序。 我在这两种情况下都有同样的错误。

看起来像 Bug 代码中的。

我同意这里可能有 Bug 。 我已经修改了添加对象setter的实现,以便正确附加到 NSMutableOrderedSet 。


- (void)addSubitemsObject:(SubItem *)value {
NSMutableOrderedSet* tempSet = [NSMutableOrderedSet orderedSetWithOrderedSet:self.subitems];
[tempSet addObject:value];
self.subitems = tempSet;
}

将设置重新分配给 self.subitems 将确保发送/didchangevalue通知。

我已经决定通过实现所有必需的方法来改进解决方案:


static NSString *const kItemsKey = @"<#property#>";

- (void)insertObject:(<#Type#> *)value in<#Property#>AtIndex:(NSUInteger)idx {
 NSIndexSet* indexes = [NSIndexSet indexSetWithIndex:idx];
 [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:kItemsKey];
 NSMutableOrderedSet *tmpOrderedSet = [NSMutableOrderedSet orderedSetWithOrderedSet:[self mutableOrderedSetValueForKey:kItemsKey]];
 [tmpOrderedSet insertObject:value atIndex:idx];
 [self setPrimitiveValue:tmpOrderedSet forKey:kItemsKey];
 [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:kItemsKey];
}

- (void)removeObjectFrom<#Property#>AtIndex:(NSUInteger)idx {
 NSIndexSet* indexes = [NSIndexSet indexSetWithIndex:idx];
 [self willChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:kItemsKey];
 NSMutableOrderedSet *tmpOrderedSet = [NSMutableOrderedSet orderedSetWithOrderedSet:[self mutableOrderedSetValueForKey:kItemsKey]];
 [tmpOrderedSet removeObjectAtIndex:idx];
 [self setPrimitiveValue:tmpOrderedSet forKey:kItemsKey];
 [self didChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:kItemsKey];
}

- (void)insert<#Property#>:(NSArray *)values atIndexes:(NSIndexSet *)indexes {
 [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:kItemsKey];
 NSMutableOrderedSet *tmpOrderedSet = [NSMutableOrderedSet orderedSetWithOrderedSet:[self mutableOrderedSetValueForKey:kItemsKey]];
 [tmpOrderedSet insertObjects:values atIndexes:indexes];
 [self setPrimitiveValue:tmpOrderedSet forKey:kItemsKey];
 [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:kItemsKey];
}

- (void)remove<#Property#>AtIndexes:(NSIndexSet *)indexes {
 [self willChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:kItemsKey];
 NSMutableOrderedSet *tmpOrderedSet = [NSMutableOrderedSet orderedSetWithOrderedSet:[self mutableOrderedSetValueForKey:kItemsKey]];
 [tmpOrderedSet removeObjectsAtIndexes:indexes];
 [self setPrimitiveValue:tmpOrderedSet forKey:kItemsKey];
 [self didChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:kItemsKey];
}

- (void)replaceObjectIn<#Property#>AtIndex:(NSUInteger)idx withObject:(<#Type#> *)value {
 NSIndexSet* indexes = [NSIndexSet indexSetWithIndex:idx];
 [self willChange:NSKeyValueChangeReplacement valuesAtIndexes:indexes forKey:kItemsKey];
 NSMutableOrderedSet *tmpOrderedSet = [NSMutableOrderedSet orderedSetWithOrderedSet:[self mutableOrderedSetValueForKey:kItemsKey]];
 [tmpOrderedSet replaceObjectAtIndex:idx withObject:value];
 [self setPrimitiveValue:tmpOrderedSet forKey:kItemsKey];
 [self didChange:NSKeyValueChangeReplacement valuesAtIndexes:indexes forKey:kItemsKey];
}

- (void)replace<#Property#>AtIndexes:(NSIndexSet *)indexes with<#Property#>:(NSArray *)values {
 [self willChange:NSKeyValueChangeReplacement valuesAtIndexes:indexes forKey:kItemsKey];
 NSMutableOrderedSet *tmpOrderedSet = [NSMutableOrderedSet orderedSetWithOrderedSet:[self mutableOrderedSetValueForKey:kItemsKey]];
 [tmpOrderedSet replaceObjectsAtIndexes:indexes withObjects:values];
 [self setPrimitiveValue:tmpOrderedSet forKey:kItemsKey];
 [self didChange:NSKeyValueChangeReplacement valuesAtIndexes:indexes forKey:kItemsKey];
}

- (void)add<#Property#>Object:(<#Type#> *)value {
 NSMutableOrderedSet *tmpOrderedSet = [NSMutableOrderedSet orderedSetWithOrderedSet:[self mutableOrderedSetValueForKey:kItemsKey]];
 NSUInteger idx = [tmpOrderedSet count];
 NSIndexSet* indexes = [NSIndexSet indexSetWithIndex:idx];
 [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:kItemsKey];
 [tmpOrderedSet addObject:value];
 [self setPrimitiveValue:tmpOrderedSet forKey:kItemsKey];
 [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:kItemsKey];
}

- (void)remove<#Property#>Object:(<#Type#> *)value {
 NSMutableOrderedSet *tmpOrderedSet = [NSMutableOrderedSet orderedSetWithOrderedSet:[self mutableOrderedSetValueForKey:kItemsKey]];
 NSUInteger idx = [tmpOrderedSet indexOfObject:value];
 if (idx!= NSNotFound) {
 NSIndexSet* indexes = [NSIndexSet indexSetWithIndex:idx];
 [self willChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:kItemsKey];
 [tmpOrderedSet removeObject:value];
 [self setPrimitiveValue:tmpOrderedSet forKey:kItemsKey];
 [self didChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:kItemsKey];
 }
}

- (void)add<#Property#>:(NSOrderedSet *)values {
 NSMutableOrderedSet *tmpOrderedSet = [NSMutableOrderedSet orderedSetWithOrderedSet:[self mutableOrderedSetValueForKey:kItemsKey]];
 NSMutableIndexSet *indexes = [NSMutableIndexSet indexSet];
 NSUInteger valuesCount = [values count];
 NSUInteger objectsCount = [tmpOrderedSet count];
 for (NSUInteger i = 0; i <valuesCount; ++i) {
 [indexes addIndex:(objectsCount + i)];
 }
 if (valuesCount> 0) {
 [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:kItemsKey];
 [tmpOrderedSet addObjectsFromArray:[values array]];
 [self setPrimitiveValue:tmpOrderedSet forKey:kItemsKey];
 [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:kItemsKey];
 }
}

- (void)remove<#Property#>:(NSOrderedSet *)values {
 NSMutableOrderedSet *tmpOrderedSet = [NSMutableOrderedSet orderedSetWithOrderedSet:[self mutableOrderedSetValueForKey:kItemsKey]];
 NSMutableIndexSet *indexes = [NSMutableIndexSet indexSet];
 for (id value in values) {
 NSUInteger idx = [tmpOrderedSet indexOfObject:value];
 if (idx!= NSNotFound) {
 [indexes addIndex:idx];
 }
 }
 if ([indexes count]> 0) {
 [self willChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:kItemsKey];
 [tmpOrderedSet removeObjectsAtIndexes:indexes];
 [self setPrimitiveValue:tmpOrderedSet forKey:kItemsKey];
 [self didChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:kItemsKey];
 }
}

而不是要做一个副本我建议要使用访问器在NSObject来获得访问到的NSMutableOrderedSet关系。


- (void)addSubitemsObject:(SubItem *)value {
 NSMutableOrderedSet* tempSet = [self mutableOrderedSetValueForKey:@"subitems"];
 [tempSet addObject:value];
 }

于网路作业系统v5.0相关指this,在核心数据发布 notes. e.g 。

在一个简短的测试中,它在我的应用程序中工作。

是,这绝对是一个核心数据 Bug 。 我写了一个ObjC-Runtime-based补丁,但当时我觉得它很快就会修复。 无论如何,没有这么幸运,所以我把它作为 KCOrderedAccessorFix 。 解决所有实体的问题:


[managedObjectModel kc_generateOrderedSetAccessors];

特别是一个实体:


[managedObjectModel kc_generateOrderedSetAccessorsForEntity:entity];

或者只是一个关系:


[managedObjectModel kc_generateOrderedSetAccessorsForRelationship:relationship];

我跟踪了 Bug,它发生在 willChangeValueForKey:withSetMutation:usingObjects:

这里调用的通知引发一系列哪个环境变量很难跟踪,当然还有更改分组为一个响应程序可能已经对另一个,这就是为什么我怀疑苹果已经做了什么都没有提示。

在一个OrderedSet装置可以malfunction,然而集和它唯一的集中,就会很好操作 这意味着只有四种方法需要改变。 因此,我所做的就是将集合操作转换为它们等价的数组操作。 这些工作完全和最小的( 但必要的) 开销。

在关键级别上,这里解决方案会遇到一个严重缺陷;如果你正在添加对象,或者一个对象已经存在,那么它要么不被添加,要么移动到已经排序列表( 我不知道)的后面。 无论哪种情况,当我们到达 didChange 时,对象预期的有序索引都不同于预期的。 这可能会破坏一些人的一些应用,但它并不影响我,因为我在添加新对象之前只添加了新对象或者确认它们的最终位置。


- (void)addChildrenObject:(BAFinancialItem *)value {
 if ([self.children containsObject:value]) {
 return;
 }
 NSIndexSet * indexSet = [NSIndexSet indexSetWithIndex:self.children.count];
 [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:indexSet forKey:ChildrenKey];
 [[self primitiveValueForKey:ChildrenKey] addObject:value];
 [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:indexSet forKey:ChildrenKey];
}

- (void)removeChildrenObject:(BAFinancialItem *)value {
 if (![self.children containsObject:value]) {
 return;
 }
 NSIndexSet * indexSet = [NSIndexSet indexSetWithIndex:[self.children indexOfObject:value]];
 [self willChange:NSKeyValueChangeRemoval valuesAtIndexes:indexSet forKey:ChildrenKey];
 [[self primitiveValueForKey:ChildrenKey] removeObject:value];
 [self didChange:NSKeyValueChangeRemoval valuesAtIndexes:indexSet forKey:ChildrenKey];
}

- (void)addChildren:(NSOrderedSet *)values {
 if ([values isSubsetOfOrderedSet:self.children]) {
 return;
 }
 NSIndexSet * indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(self.children.count, values.count)];
 [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:indexSet forKey:ChildrenKey];
 [[self primitiveValueForKey:ChildrenKey] unionOrderedSet:values];
 [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:indexSet forKey:ChildrenKey];
}

- (void)removeChildren:(NSOrderedSet *)values {
 if (![self.children intersectsOrderedSet:values]) {
 return;
 }
 NSIndexSet * indexSet = [self.children indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
 return [values containsObject:obj];
 }];
 [self willChange:NSKeyValueChangeRemoval valuesAtIndexes:indexSet forKey:ChildrenKey];
 [[self primitiveValueForKey:ChildrenKey] minusOrderedSet:values];
 [self didChange:NSKeyValueChangeRemoval valuesAtIndexes:indexSet forKey:ChildrenKey];
}

当然,有一个更简单的解决方案。 它如下所示:


- (void)addChildrenObject:(BAFinancialItem *)value {
 if ([self.children containsObject:value]) {
 return;
 }
 [self insertObject:value inChildrenAtIndex:self.children.count];
}

- (void)removeChildrenObject:(BAFinancialItem *)value {
 if (![self.children containsObject:value]) {
 return;
 }
 [self removeObjectFromChildrenAtIndex:[self.children indexOfObject:value]];
}

- (void)addChildren:(NSOrderedSet *)values {
 if ([values isSubsetOfOrderedSet:self.children]) {
 return;
 }
 [self insertChildren:values atIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(self.children.count, values.count)]];
}

- (void)removeChildren:(NSOrderedSet *)values {
 if (![self.children intersectsOrderedSet:values]) {
 return;
 }
 [self removeChildrenAtIndexes:[self.children indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
 return [values containsObject:obj];
 }]];
}

收到同样的错误,@LeeIII 解决方案为我工作( 致谢) 。 我建议稍微修改一下:

  • 使用 objective-c 类别存储新方法( 如果再次生成项目,我们将不会丢失方法)
  • 检查我们是否已经有可变设置

Item+category.m的内容:


#import"Item+category.h"

@implementation Item (category)

- (void)addSubitemsObject:(SubItem *)value {
 if ([self.subitems isKindOfClass:[NSMutableOrderedSet class]]) {
 [(NSMutableOrderedSet *)self.subitems addObject:value];
 } else {
 NSMutableOrderedSet* tempSet = [NSMutableOrderedSet orderedSetWithOrderedSet:self.subitems];
 [tempSet addObject:value];
 self.subitems = tempSet;
 }
}

@end

如果你使用的是 mogenerator,则不是


[parentObject add<Child>sObject:childObject];

只需使用:


[[parent object <child>sSet] addObject:childObject];

我亲自调用了CoreData生成方法的调用,直接调用了 @Stephan: 中的方法,方法如下


NSMutableOrderedSet* tempSet = [self mutableOrderedSetValueForKey:@"subitems"];
 [tempSet addObject:value];
[tempSet addObject:value];

这样就无需在 Bug 时的范畴可能更高版本的冲突与解决方案从苹果到生成的代码是固定的。

这已经添加了作为官方方式的添加 !

文档到许多关系 说: 你应该访问代理可变集或者有序集,使用


NSMutableOrderedSet * set = [managedObject mutableOrderedSetValueForKey:@"toManyRelation"];

修改这里设置将添加或者删除与你的托管对象的关系。 使用访问器访问可变有序集,无论使用 [ ] 还是。 符号错误并将失败。

...