在NSOrderedSet生成的访问器中引发异常


364

在我的Lion应用程序上,我具有以下数据模型:

在此处输入图片说明

subitems内部的关系Item 是有序的

Xcode的4.1(编译4B110)为我创建的文件Item.hItem.mSubItem.hSubItem.h

以下是内容(自动生成)Item.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

这是(自动生成的)内容Item.m

#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

你能帮助我吗?

更新:

从我的错误报告发出1,787天后,今天(2016年8月1日),Apple给我写了这句话:“请使用最新的iOS 10 beta版本验证此问题,并在bugreport.apple.com上更新错误报告,并提供您的结果。” 。我们希望这是正确的时机:)


5
我看到了同样的问题。希望很快能解决。尽管暂时直接使用可变的有序集是一个简单的解决方法。注意:我使用的是生成器,但是我假设它在内部针对该部分生成的代码使用相同的Apple生成器。
乍得Podoski

12
已经快两年了!您会在iOS 7,Apple中修复它吗?-我只想与那些想知道这个错误是否仍然存在的人分享:“是的。”
2013年

1
现在已经接近两年了,这在所有xcode 5开发人员预览版中仍然是一个问题。
Korvin Szanto

2
如果使用适当的KVC访问器,您仍然看到问题吗?(ie mutableOrderedSetValueForKey:
让人生气的2014年

3
在小牛看来仍然是个问题。
蒂姆(Tim)2014年

Answers:


263

我用您的数据模型以及我自己的一个用不同名称重现了您的设置。在这两种情况下,我都遇到相同的错误。

看起来像是苹果自动生成代码中的错误。


60
错误ID是10114310。据报道,此错误发生在2011年9月13日,但今天(2012年1月15日)仍然处于“打开”状态。考虑到有相同问题的人数,这真是令人难以置信。
开发

14
更新:今天(2012年5月11日),在我的报告(2011年9月13日)过去241天后,错误#10114310仍然打开。难以置信的。
开发人员

23
我只是在WWDC的CoreData Lab会话中与一位Apple工程师讨论了这一点。他们承认这个问题,并且它是一个真正的错误,并且从我所看到的来看,它具有“严重”状态,但是对于何时修复它,当然没有任何保证。我认为iOS6 / Mountain Lion中不会修复该问题。我认为最好进一步复制此雷达。目前大约有25个dup,越多越好!
DaGaMs 2012年

40
今天刚刚检查过,它仍然存在于iOS 7 GM / OMG中!我简直不敢相信…
an0 2013年

79
更新:自从我填补bug#10114310以来,797天,2个新的主要iOS版本和无数Xcode版本已经过去。而且它仍然是“开放的”。难以置信的。
2013年

244

我同意这里可能存在错误。我已经修改了添加对象设置器的实现,以将其正确附加到NSMutableOrderedSet中。

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

将集合重新分配给self.subitems将确保发送Will / DidChangeValue通知。


您的代码段正是解决这个问题所需要的。希望苹果公司最终解决该问题,但到目前为止,我还没有看到使用您的方法的任何问题。
Christopher Hujanen 2011年

我尝试实现此替代方法时遇到此错误。[__NSArrayI isEqualToSet:]:无法识别的选择器已发送到实例...这通常来自已发布但无法找到的地方,任何人都可以运行入这个?
DerekH 2012年

@DerekH isEqualToSet是只有NSSet才有的方法,所以我猜您是在将指针转换回NSManagedObject之前将其转换,创建或将其视为NSArray,如果有任何原因应调用isEqualToOrderedSet以确定该集合是否需要甚至改变或保持原样。
InitJason 2012年

3
@MarkAmery经过测试。已验证。动态设置器self.subitems确实会发送通知。因此,JLust解决方案是正确的。
bernstein

3
这是一个很好的答案,但是效率很低。您复制整个有序集,进行修改,然后再复制回去。效果不仅是对有序集的打击,而且还会发出通知,通知说,每当有序集被更改时,其整个内容都会被更改!例如,如果将此有序集用于UITable,则可能会对更新产生严重影响。我已经在解决方案中概述了错误的确切来源,并且展示了一种更有效的绕过错误的方法。
欧文·戈弗雷

111

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

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];
    }
}

1
什么是崩溃类型?'removeObjectFromSubitemsAtIndex'不会删除这些子项,它们仍然存在于您的存储中,这只是删除对象之间关系的一种方式。
德米特里·马卡连科

2
kItemsKey是一个常数,仅为了方便起见在KVO方法调用中添加了该常数。这是您为其编写方法的有序关系的名称。
德米特里·马卡连科

1
我就是这么想的。谢谢。但是我的问题是,使用这些方法不能将数据保存到数据库中。
Bagusflyer 2012年

4
!!!!!!!!!!!! 只需复制代码并更改方法名称,即可完美运行!!!这是最快的答案。
flypig

1
这很棒,但是不需要创建有序集的临时副本。罪魁祸首是willChangeValueForKey:withSetMutation:usingObjects,您已成功避免了它。之后,请使用[[self primitiveValueForKey:ChildrenKey] unionOrderedSet:values][[self primitiveValueForKey:ChildrenKey] minusOrderedSet:values]适当使用。请参阅我的答案以获取详细信息。
欧文·戈弗雷

38

是的,这绝对是一个核心数据错误。不久前,我写了一个基于ObjC-Runtime的修复程序,但是当时我认为它很快就会修复。无论如何,没有运气,所以我将其发布在GitHub上,名称KCOrderedAccessorFix。解决所有实体上的问题:

[managedObjectModel kc_generateOrderedSetAccessors];

特别是一个实体:

[managedObjectModel kc_generateOrderedSetAccessorsForEntity:entity];

或仅出于一种关系:

[managedObjectModel kc_generateOrderedSetAccessorsForRelationship:relationship];

我想知道这是否与Apple的实际解决方案冲突?
2013年

3
这不应该与Apple的修复程序冲突,因为它的目的是无论如何都要超越Apple的实现。如果这是由Apple实际解决的,也许我会添加- (BOOL)kc_needsOrderedSetAccessorFix;或检查Foundation / iOS版本的内容。
Sterling Archer 2014年

2
CocoaPods主存储库中已经有一个KCOrderedAccessorFix.podspec。因此,为了将其链接到您的项目,您只需将“ pod'KCOrderedAccessorFix'”添加到您的Podfile中
Anton Matosov 2014年

iOS 8存在一些问题(的方法签名不正确objc_msg_send
NSTJ 2014年

在iOS9上可以正常工作!这是有史以来最好的解决方案,无需更改代码!
Borzh 2015年

32

我建议不要复制,而建议使用NSObject中的访问器来访问关系的NSMutableOrderedSet。

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

例如,iOS v5.0核心数据发行说明参考了此内容。

在简短的测试中,它在我的应用程序中有效。


1
无法轻松地重构文字字符串。如果使用代码,则编译器可以键入check self.subitems。
logancautrell,2012年

1
@logancautrell是的,这是正确的。这取决于特定用例的优先级。通常,我专注于节省资源,尤其是在这种情况下,因为这只是一种解决方法。
Stephan 2012年

2
文本字符串可以被替换NSStringFromSelector(@selector(subitems)):)虽然
杰克

17

我已经跟踪了该错误。它发生在willChangeValueForKey:withSetMutation:usingObjects:

该调用会触发一连串的通知,这些通知可能很难跟踪,当然,更改一个响应者可能会对另一个响应产生影响,我怀疑这就是为什么苹果没有采取任何措施。

但是,这在Set中是可以的,并且只有在OrderedSet上发生故障的Set操作才可以。这意味着仅需要更改四种方法。因此,我所做的只是将Set操作转换为等效的Array操作。这些工作完美且开销最小(但必要)。

从关键的角度来看,此解决方案确实存在一个关键缺陷。如果要添加对象,并且其中一个对象已经存在,则不添加对象或将其移到有序列表的后面(我不知道哪个)。无论哪种情况,到我们到达时对象的预期有序索引都与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];
    }]];
}

太可惜了,其他所有人似乎都忽略了这个答案,绝对是最好的解决方案。
乔治

与使用orderedSetWithOrderedSet进行本地设置的解决方案相比,此解决方案的性能要好得多。当您拥有大数据集时,这会带来很大的开销。较简单的解决方案似乎只是初始方法的重构版本,而未显示方法。
David Pettigrew 2014年

1
我仍然看到崩溃的addChildren:***终止应用程序由于未捕获的异常“NSInvalidArgumentException”,原因是:“ - [TrackHistory insertTrackpoints:atIndexes:]:发送到实例0x1702b1b20无法识别的选择”
维克多·波格丹

@OwenGodfrey对于更简单的解决方案,您在哪里实现这些方法?我遇到一个异常:[父insertObject:inChildrenAtIndex:]无法识别的选择器发送到实例0x6180000ac480。
Dalmazio

您的变量是“父项”,大写字母“ P”?这是否意味着您将类称为“ Parent”,或者您有一个名为“ Parent”的实例变量?如果我的班级是Parent,那么我将在Parent的底部实现此方法,但是您需要在一个实例上调用它,因为它不是类方法,因此它更可能以小写的“ p”命名为“ parent” 。
欧文·戈弗雷

10

苹果文档的许多关系说:你应该访问代理可变设定或使用有序集合

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

修改此集合将向您的托管对象添加或删除关系。使用[]或。使用访问器访问可变有序集。表示法错误,将失败。


3
公平地说,文档还说:“或自动生成的关系变异器方法之一(请参阅动态生成的访问器方法):”
Matt

好吧好吧...你是对的。那么,那可以说这是最简单的工作方式……
Nicolas Manzini 2014年

9

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

  • 使用Objective-C类别存储新方法(因此,如果再次生成Item,我们将不会丢失方法)
  • 检查我们是否已经设置了可变

内容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

将此代码移入类别的好方法。但是我们仍然需要使用will / setPrimitiveValue / didChange调用来包含实际的添加/删除操作,例如@Dmitry Makarenko答案中的那样。
弗拉基米尔·舒秋克

8

如果您使用的是发电机,则代替

[parentObject add<Child>sObject:childObject];

只需使用:

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

因为mogenerator会处理额外的代码,否则您将不得不编写代码,并且仅允许访问基础set对象。
Καrτhικ

看起来好像已经提交了一个修复程序,这意味着发电机将生成校正后的物体... github.com/dmakarenko/mogenerator/commit/…–
组合

1
我正在使用,mogenerator但仍然有错误。
科拉斯(Colas)2013年

7

我个人刚刚将对CoreData生成的方法的调用替换为对@Stephan在另一个解决方案中概述的方法的直接调用:

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

修复了错误之后,无需使用以后可能与Apple的解决方案相冲突的类别。

这是作为官方方法的额外好处!


这将产生以下错误:'[<CLASS 0x20886d10> valueForUndefinedKey:]:此类对于键子项不是与键值兼容的类。
jmstone617 2012年

虽然这仍然让我感到无穷无尽,但苹果公司的已知问题中没有列出这一点(我为它似乎毫无用处的手势打开了雷达),但这种解决方案对我来说是完美无缺的。
Scott Corscadden

希望我早些时候看到了这个答案;我用的是最高的投票答案,直到我最近做了一些挖掘,终于实现你到底是什么在这里:)
杰克

为什么addObject:叫两次?
杰森·摩尔

5

看来,如果通过将父级设置为子级来将父级与子级链接,则相反,它不会崩溃。

因此,如果您这样做:

[child setParent:parent]

代替

[parent setChildObects:child]

它应该可以运行,至少它可以在iOS 7上运行,并且在关系上没有任何问题。


1
当双方都多时,并没有太大的好处。那时没有明确的亲子关系。
fatuhoku 2014年

3

我遇到了同样的问题,但是只有当我尝试了与自己所做的不同的事情时。我看不到subItem的代码,但我将假定它具有指向项目的反向链接。让我们称其为尊敬的链接“ parentItem”,那么最简单的解决方案是:

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

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

//[item addSubitemsObject:subItem];
subItem.parentItem = item;

效果是它利用了苹果自己的代码,并且简单干净。此外,该集会自动添加到其中,并且所有观察者都会更新。没问题。


很好 它解决了整个问题,并使它井井有条。该错误仍然存​​在仍然很疯狂。这个答案的另一个好处是,如果您重新生成核心数据模型,则不必重写错误修复程序。谢谢!
Johan S

看到我的其他答案。我更详细地跟踪了该错误。这仍然是最简单的方法,但是另一种方法是最好的,因为它提供了更多的可能性。
Owen Godfrey

哇!最后!!!谢谢!(尝试过您的其他代码,但出现错误,在[self didChange:NSKeyValueChangeInsertion valuesAtIndexes:indexSet forKey:ChildrenKey]处发送了有关该错误类型的信息;)
Leonard Pauli

3

我只是犯了这个问题,并使用比此处概述的其他解决方案简单得多的实现解决了这个问题。NSManagedObject当不使用子类时,我只是简单地利用可用于处理关系的方法。

将实体插入NSOrderedSet关系中的示例实现如下所示:

- (void)addAddress:(Address *)address
{
    if ([self.addresses containsObject:address]) {
        return;
    }
    // Use NSManagedObject's methods for inserting an object
    [[self mutableOrderedSetValueForKey:@"addresses"] addObject:address];
}

这完美地工作了,这是我移入NSManagedObject子类之前使用的东西。


3

当我使用XCode 7将项目从Objective-C迁移到Swift 2时,发生了这个问题。该项目曾经可以运行,并且有一个很好的理由:我正在使用MOGenerator,它具有替换方法来修复此错误。但并非所有方法都需要替换。

因此,这是带有示例类的完整解决方案,它尽可能地依赖默认访问器。

假设我们有一个包含订购商品的清单

首先,如果您有一对多关系,则是一个快速的胜利,最简单的方法是:

item.list = list

代替

list.addItemsObject(item)

现在,如果这不是一个选择,那么您可以执行以下操作:

// Extension created from your DataModel by selecting it and
// clicking on "Editor > Create NSManagedObject subclass…"

extension List {
  @NSManaged var items: NSOrderedSet?
}

class List

  // Those two methods work out of the box for free, relying on
  // Core Data's KVC accessors, you just have to declare them
  // See release note 17583057 https://developer.apple.com/library/prerelease/tvos/releasenotes/DeveloperTools/RN-Xcode/Chapters/xc7_release_notes.html
  @NSManaged func removeItemsObject(item: Item)
  @NSManaged func removeItems(items: NSOrderedSet)

  // The following two methods usually work too, but not for NSOrderedSet
  // @NSManaged func addItemsObject(item: Item)
  // @NSManaged func addItems(items: NSOrderedSet)

  // So we'll replace them with theses

  // A mutable computed property
  var itemsSet: NSMutableOrderedSet {
    willAccessValueForKey("items")
    let result = mutableOrderedSetValueForKey("items")
    didAccessValueForKey("items")
    return result
  }

  func addItemsObject(value: Item) {
    itemsSet.addObject(value)
  }

  func addItems(value: NSOrderedSet) {
    itemsSet.unionOrderedSet(value)
  }
end

当然,如果您使用的是Objective-C,则可以做完全相同的事情,因为这是我首先想到的地方:)


3

我同意这里可能存在错误。我已经修改了添加对象> setter的实现,以将其正确附加到NSMutableOrderedSet中。

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

将集合重新分配给self.subitems将确保发送Will / DidChangeValue通知>。

Leelll,您确定在对存储在该集中的NSMutableOrderedSet值进行自定义设置后,CoreData会正确将其保存到数据库中吗?我没有检查,但看起来CoreData对NSOrderedSet一无所知,并希望NSSet作为多对多关系容器。


为了使CoreData返回或获取NSOrderedSet对象,必须满足多个条件,如开始的问题所示。当人们共享我的代码时,我看到的最常见错误是开发人员未运行Lion。NSOrderedSets框架在Snowleopard上不可用。但是,是的,尽管我不确定这在性能上是最好的,但我没有看到失败。我猜想这将整个集合并替换掉它,而不仅仅是插入所需的记录。
InitJason

2

我认为每个人都错过了真正的问题。它不是在访问器方法中,而是在事实中NSOrderedSet不是的子类NSSet。因此,当-interSectsSet:使用有序集作为参数调用时,它将失败。

NSOrderedSet* setA = [NSOrderedSet orderedSetWithObjects:@"A",@"B",@"C",nil];
NSSet* setB = [NSSet setWithObjects:@"C",@"D", nil];

 [setB intersectsSet:setA];

失败于 *** -[NSSet intersectsSet:]: set argument is not an NSSet

解决方法似乎是更改set运算符的实现,以便它们透明地处理类型。没有理由为什么-intersectsSet:应该与有序或无序集合一起工作。

异常发生在更改通知中。大概在处理逆关系的代码中。因为只有在我设置了逆关系时才会发生。

以下对我有用

@implementation MF_NSOrderedSetFixes

+ (void) fixSetMethods
{
    NSArray* classes = [NSArray arrayWithObjects:@"NSSet", @"NSMutableSet", @"NSOrderedSet", @"NSMutableOrderedSet",nil];

    [classes enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        NSString* name = obj;
        Class aClass = objc_lookUpClass([name UTF8String]);
        [MF_NSOrderedSetFixes fixMethodWithSetArgument:@selector(intersectsSet:) forClass:aClass];
        [MF_NSOrderedSetFixes fixMethodWithSetArgument:@selector(isSubsetOfSet:) forClass:aClass];
    }];
}

typedef BOOL (*BoolNSetIMP)(id _s,SEL sel, NSSet*);

/*
    Works for all methods of type - (BOOL) method:(NSSet*) aSet
*/
+ (void) fixMethodWithSetArgument:(SEL) aSel forClass:(Class) aClass 
{
    /* Check that class actually implements method first */
    /* can't use get_classInstanceMethod() since it checks superclass */
    unsigned int count,i;
    Method method = NULL;
    Method* methods = class_copyMethodList(aClass, &count);
    if(methods) {
        for(i=0;i<count;i++) {
            if(method_getName(methods[i])==aSel) {
                method = methods[i];
            }
        }
        free(methods);
    }
    if(!method) {
        return;
    }

   // Get old implementation
   BoolNSetIMP originalImp  = (BoolNSetIMP) method_getImplementation(method);
   IMP newImp = imp_implementationWithBlock(^BOOL(NSSet *_s, NSSet *otherSet) {
        if([otherSet isKindOfClass:[NSOrderedSet class]]) {
            otherSet = [(NSOrderedSet*)otherSet set];
        }
        // Call original implementation
        return originalImp(_s,aSel,otherSet);
    });
    method_setImplementation(method, newImp);
}
@end

2

我刚在Swift(Xcode 6.1.1)中遇到问题。

答案是不要编码任何方法或其他东西在您的NSManagedObject子类中。我认为这是一个编译器错误。非常奇怪的错误..

希望能帮助到你 ..


3
因此,如果我无法实现其他修复程序,该怎么做才能解决此问题?
Ben Leggiero

2

我通过将Inverse设置为No Inverse解决了这个问题,我不知道为什么,也许是Apple Bug。在此处输入图片说明


1

我遇到的情况与“信号”而不是“子项目”相同。使用tempset的解决方案在我的测试中有效。此外,我在removeSignals:方法上遇到了问题。这种覆盖似乎有效:

- (void)removeSignals:(NSOrderedSet *)values {
    NSMutableOrderedSet* tempset = [NSMutableOrderedSet orderedSetWithOrderedSet:self.signals];
    for (Signal* aSignal in values) {
        [tempset removeObject:aSignal];
    }
    self.signals = tempset;
}

如果有更好的方法可以进行此操作,请告诉我。我输入的值从不超过10 -20个项目,因此性能并不是什么大问题-尽管如此,请指出任何相关信息。

谢谢,

达米安


1

我通过搜索错误消息找到了这个问题,只是想指出我以略有不同的方式遇到了此错误(不使用有序集)。这并不是对给定问题的答案,但是我将其发布在这里,以防万一对在搜索过程中偶然发现该问题的其他人有所帮助。

我正在添加一个新的模型版本,并向现有模型添加了一些关系,并且自己在头文件中定义了add * Object方法。当我尝试给他们打电话时,我得到了上面的错误。

在查看了模型之后,我意识到我已经愚蠢地忘记了选中“一对多关系”复选框。

因此,如果您遇到了这种情况并且没有使用有序集,请仔细检查模型。


1

我找到了一个对我有用的错误修复程序。我只是替换为:

[item addSubitemsObject:subItem];

有了这个:

item.subitemsObject = subItem;

1

SWIFT中正确答案的更好版本

var tempSet = NSMutableOrderedSet()
if parent!.subItems != nil {
    tempSet = NSMutableOrderedSet(orderedSet: parent!.subItems!)
}

tempSet.add(newItem)
parent!.subItems = tempSet

0

我发现使用LeeIII的方法行得通,但是在进行分析时发现它非常慢。解析1000个项目花了15秒。注释掉添加关系的代码,将15秒变成2秒。

我的解决方法(速度更快但更难看)包括创建一个临时的可变数组,然后在完成所有解析后复制到有序集合中。(如果要添加许多关系,这只是性能上的胜利)。

@property (nonatomic, retain) NSMutableArray* tempItems;
 ....
@synthesize tempItems = _tempItems;
 ....

- (void) addItemsObject:(KDItem *)value 
{
    if (!_tempItems) {
        self.tempItems = [NSMutableArray arrayWithCapacity:500];
    }
    [_tempItems addObject:value];
}

// Call this when you have added all the relationships
- (void) commitRelationships 
{
    if (_tempItems) {
        self.items = [NSOrderedSet orderedSetWithArray:self.tempItems];
        self.tempItems = nil;
    }
}

希望对别人有帮助!


0

罗伯特

我同意您的答案可以解决此问题,但是请记住,已经有一种自动创建的方法,用于向关系中添加整套值。苹果公司的文件(如这里看到在“一对多关系”一节或在这里实现它们这种方式下的“自定义一对多关系存取方法”部分):

- (void)addEmployees:(NSSet *)value
{
[self willChangeValueForKey:@"employees"
      withSetMutation:NSKeyValueUnionSetMutation
      usingObjects:value];
[[self primitiveEmployees] unionSet:value];
[self didChangeValueForKey:@"employees"
      withSetMutation:NSKeyValueUnionSetMutation
      usingObjects:value];
}

- (void)removeEmployees:(NSSet *)value
{
[self willChangeValueForKey:@"employees"
      withSetMutation:NSKeyValueMinusSetMutation
      usingObjects:value];
[[self primitiveEmployees] minusSet:value];
[self didChangeValueForKey:@"employees"
      withSetMutation:NSKeyValueMinusSetMutation
      usingObjects:value];
}

您可以轻松地在核心数据之外编译一组关系,然后使用此方法一次将其全部添加。它可能不如您建议的方法难看;)


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.