NSOrderedSet生成的访问器抛出异常

Dev*_*Dev 364 cocoa core-data xcode4

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

在此输入图像描述

subitems里面的关系Item 是有序的.

的Xcode 4.1(建4B110)为我创建的文件Item.h,Item.m,SubItem.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
Run Code Online (Sandbox Code Playgroud)

以下是内容(自动生成)Item.m:

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

@implementation Item

@dynamic name;
@dynamic subitems;

@end
Run Code Online (Sandbox Code Playgroud)

如您所见,该类Item提供了一个名为的方法addSubitemsObject:.不幸的是,当试图以这种方式使用它时:

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

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

[item addSubitemsObject:subItem];
Run Code Online (Sandbox Code Playgroud)

出现此错误:

2011-09-12 10:28:45.236 Test[2002:707] *** -[NSSet intersectsSet:]: set argument is not an NSSet
Run Code Online (Sandbox Code Playgroud)

你能帮助我吗?

更新:

在我的错误报告发布后仅1,787天,今天(2016年8月1日),Apple给我写了这样的信息:"请使用最新的iOS 10 beta版本验证此问题,并在bugreport.apple.com上更新您的错误报告." .我们希望这是正确的时间:)

Tec*_*Zen 263

我使用您的数据模型和我自己的不同名称之一重现了您的设置.在这两种情况下我都得到了同样的错误.

看起来像Apple的自动生成代码中的错误.

  • **更新:**797天,2个新的主要iOS版本,以及无数的Xcode版本已经过去了,因为我填写了错误#10114310.它仍然是"开放的".难以置信的. (79认同)
  • 错误ID是10114310.据报道于2011年9月13日,但今天(2012年1月15日)它仍然"开放".考虑到有同样问题的人数,这是令人难以置信的. (60认同)
  • 刚刚查了一下,它仍然存在于iOS 7 GM/OMG中!我简直不敢相信...... (40认同)
  • 我刚刚在WWDC的一个CoreData实验室会议期间与一位Apple工程师一起提出了这个问题.他们承认这个问题并且这是一个真正的错误,从我所看到它具有"关键"状态,但当然没有任何关于他们什么时候修复它的承诺.我认为它不会在iOS6/Mountain Lion中修复.我认为进一步复制这种雷达会很好.目前它有大约25个重复,越多越好! (23认同)
  • 更新:今天(2012年5月11日)错误#10114310仍在我的报告(2011年9月13日)后241天仍然开放.难以置信的. (14认同)
  • **更新:**3岁生日快乐!两天前NSOrderedSet的错误变成了三个:) (12认同)
  • 嘿伙计们,我还和你在一起,在iOS 9中! (9认同)
  • 已在iOS 10中修复!NSManagedObject动态访问器生成已修复问题,以正确生成有序到多个关系的访问器.https://developer.apple.com/library/prerelease/content/releasenotes/General/WhatNewCoreData2016/ReleaseNotes.html#//apple_ref/doc/uid/TP40017342-CH1-DontLinkElementID_3 (9认同)
  • 谢谢.我提交了一份错误报告. (5认同)
  • 几乎两年后,Apple确认这个关键错误仍然存​​在:"这个问题仍在调查中.如果我们有更新或需要更多信息,我们会与您联系." 希望他们在iOS 7中修复它. (4认同)
  • 我无法相信这.刚刚点击这个.花一整天调试,因为我认为我的添加逻辑有一个缺陷.谢谢Crapple. (4认同)
  • **更新:**我的错误报告后仅1,787天,今天(2016年8月1日)Apple给我写了这样的信息:"请使用最新的iOS 10 beta版本验证此问题,并通过https:// bugreport更新您的错误报告.apple.com /结果." 我们希望这是正确的时间:) (4认同)
  • 每个人都应该向Apple提交一个重复的错误.重复的错误是Apple工程师评估错误修复优先级的主要方式之一. (3认同)
  • 2015年2月9日.仍然没有修复.AppLOL :) (3认同)
  • 这是令人伤心的苹果,经过将近4年的时间,这个bug仍然存在于核心数据的核心部分 (3认同)
  • **更新:**自从我填写错误#10114310以来已经过了442天,猜猜怎么着?今天(2012年11月28日)它仍然开放.我也相信苹果似乎不想解决这个问题. (2认同)
  • 仍在iOS 8.0b3上 (2认同)
  • 我在2012年末遇到了这个问题并且我搜索了这个问题,现在是2015年,在iOS 8中,这个bug仍然存在,每次我在我的余生中遇到这个bug时,我都会发表评论! ! (2认同)

Ini*_*son 244

我同意这里可能存在错误.我已经修改了add object setter的实现,以便正确地追加到NSMutableOrderedSet.

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

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

  • @MarkAmery经过测试.验证.动态setter self.subitems会发送通知.所以JLust解决方案是正确的. (3认同)
  • 这是一个很好的答案,但效率低下.您复制整个有序集,修改然后将其复制回来.效果不仅仅是对有序集的命中,而是发出通知,每次更改有序集时,其全部内容都会更改!例如,如果将此有序集用于UITable,则可能会产生严重的更新后果.我在我的解决方案中概述了错误的确切位置,并且我已经展示了绕过错误的更有效方法. (3认同)

Dmi*_*nko 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];
    }
}
Run Code Online (Sandbox Code Playgroud)

  • !!!!!!!!! 只需复制代码并更改方法名称,它就可以完美运行!这是最快的答案. (4认同)
  • kItemsKey是一个常量,它是为了方便KVO方法调用而添加的.它是您编写方法的有序关系的名称. (2认同)

Ste*_*her 38

是的,这绝对是一个核心数据错误.我曾经写过一个基于ObjC-Runtime的修复程序,但当时我认为很快就会修复它.无论如何,没有这样的运气,所以我把它作为KCOrderedAccessorFix发布在GitHub上.解决所有实体的问题:

[managedObjectModel kc_generateOrderedSetAccessors];
Run Code Online (Sandbox Code Playgroud)

特别是一个实体:

[managedObjectModel kc_generateOrderedSetAccessorsForEntity:entity];
Run Code Online (Sandbox Code Playgroud)

或者只是为了一个关系:

[managedObjectModel kc_generateOrderedSetAccessorsForRelationship:relationship];
Run Code Online (Sandbox Code Playgroud)

  • 这不应该与Apple的修复冲突,因为它的目的是无论如何都要覆盖Apple的实现.当/如果这实际上由Apple修复时,我可能会添加` - (BOOL)kc_needsOrderedSetAccessorFix;`或者检查Foundation/iOS版本的东西. (3认同)
  • CocoaPods主仓库中已经有一个KCOrderedAccessorFix.podspec.因此,为了将其链接到您的项目,您只需将"pod"KCOrderedAccessorFix'"添加到您的Podfile中 (2认同)

Ste*_*han 32

而不是制作副本我建议使用NSObject中的访问器来访问关系的NSMutableOrderedSet.

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

例如,适用于iOS v5.0Core Data发行说明请参阅此内容.

在一个简短的测试中它适用于我的应用程序.

  • 文字字符串可以替换为`NSStringFromSelector(@selector(subitems))`但是:) (2认同)

Owe*_*rey 17

我已经跟踪了这个错误.它发生在willChangeValueForKey:withSetMutation:usingObjects:.

此呼叫引发了一系列可能难以跟踪的通知,当然,对一个响应者的更改可能会对另一个响应者产生影响,我怀疑这是Apple没有做任何事情的原因.

但是,在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];
}
Run Code Online (Sandbox Code Playgroud)

当然,有一个更简单的解决方案.它如下;

- (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];
    }]];
}
Run Code Online (Sandbox Code Playgroud)


Nic*_*ini 10

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

NSMutableOrderedSet * set = [managedObject mutableOrderedSetValueForKey:@"toManyRelation"];
Run Code Online (Sandbox Code Playgroud)

修改此集将添加或删除与托管对象的关系.使用访问器访问可变有序集,无论是[]还是.符号是错误的,会失败.

  • 公平地说,文档还说:"或者自动生成的关系变异器方法之一(参见动态生成的访问器方法):" (3认同)

小智 9

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

  • 使用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
Run Code Online (Sandbox Code Playgroud)


Καr*_*hικ 8

如果您使用的是发电机,那么请使用

[parentObject add<Child>sObject:childObject];
Run Code Online (Sandbox Code Playgroud)

只需使用:

[[parent object <child>sSet] addObject:childObject];
Run Code Online (Sandbox Code Playgroud)


Gro*_*hal 7

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

NSMutableOrderedSet* tempSet = [self mutableOrderedSetValueForKey:@"subitems"];
      [tempSet addObject:value];
[tempSet addObject:value];
Run Code Online (Sandbox Code Playgroud)

这样就不再需要在修复错误时可能与Apple的解决方案冲突的类别.

这有额外的优势,是正式的方式!


Cat*_*ata 5

看起来如果你通过将父级设置为子级而将父级与子级相关联而不是相反的方式将其连接起来而不会崩溃.

所以,如果你这样做:

[child setParent:parent]
Run Code Online (Sandbox Code Playgroud)

代替

[parent setChildObects:child]
Run Code Online (Sandbox Code Playgroud)

它应该工作,至少它适用于iOS 7,并没有任何问题的关系.