NSOrderedSet 生成的访问器中抛出的异常

Posted

技术标签:

【中文标题】NSOrderedSet 生成的访问器中抛出的异常【英文标题】:Exception thrown in NSOrderedSet generated accessors 【发布时间】:2011-09-12 08:59:25 【问题描述】:

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

Item 内部的关系subitems是有序的

Xcode 4.1 (build 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 上更新您的错误报告与你的结果。”。让我们希望这是正确的时间:)

【问题讨论】:

我遇到了同样的问题。希望它很快得到修复。尽管暂时直接使用可变有序集是一种简单的解决方法。注意:我使用的是 mogenerator,但我假设它在内部使用相同的 Apple 生成器来生成这部分代码。 快2年了!你会在 iOS 7 中修复它吗,Apple? ——我只是想和那些想知道这个bug是否仍然存在的人分享一下:“是的,它是。” 现在已经快两年了,这仍然是所有 xcode 5 开发者预览版中的一个问题。 如果您使用适当的 KVC 访问器,您是否仍然看到问题? (即mutableOrderedSetValueForKey: 在小牛队似乎仍然是一个问题。 【参考方案1】:

我使用您的数据模型和我自己的一个不同名称的数据模型复制了您的设置。在这两种情况下我都遇到了同样的错误。

看起来像是 Apple 自动生成的代码中的一个错误。

【讨论】:

错误 ID 为 10114310。它于 2011 年 9 月 13 日报告,但今天(2012 年 1 月 15 日)它仍然“开放”。考虑到有同样问题的人数,这太不可思议了。 更新:今天(2012 年 5 月 11 日)在我报告(2011 年 9 月 13 日)241 天后,错误 #10114310 仍然存在。难以置信。 我刚刚在 WWDC 的 CoreData Lab 会议上向一位 Apple 工程师提出了这个问题。他们承认这个问题并且这是一个真正的错误,据我所知,它处于“严重”状态,但当然没有承诺他们何时会修复它。我认为它不会在 iOS6/Mountain Lion 中得到修复。我认为进一步复制这个雷达会很好。目前它有大约 25 个 dup,越多越好! 今天刚查了一下,iOS 7 GM/OMG 里还是有的!我不敢相信…… 更新: 797 天,2 个新的主要 iOS 版本,以及无数 Xcode 版本已经过去了,因为我填补了错误 #10114310。它仍然是“开放的”。难以置信。【参考方案2】:

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

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

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

【讨论】:

您的代码 sn-p 正是我解决此问题所需要的。希望 Apple 最终解决这个问题,但到目前为止,我还没有看到使用你的方法有任何问题。 当我尝试实现此解决方法时收到此错误。[__NSArrayI isEqualToSet:]: unrecognized selector sent to instance... 这通常来自已发布的项目,但是找不到在哪里,有人碰到这个吗? @DerekH isEqualToSet 是只有 NSSet 拥有的方法,所以我的猜测是您在传递回 NSManagedObject 之前已经转换、创建或将指针视为 NSArray,如果有任何原因,这应该是调用 isEqualToOrderedSet 来确定该集合是否需要更改或保持原样。 @MarkAmery 已测试。已验证。动态设置器 self.subitems 确实发送通知。所以 JLust 的解决方案是正确的。 这是一个很好的答案,但效率低下。您复制整个有序集,修改然后将其复制回来。效果不仅是对有序集的命中,而且会发出通知,每次有序集改变时,它的全部内容都会改变!例如,如果将此有序集用于 UITable,则可能会对更新产生严重影响。我已经在我的解决方案中概述了错误的确切来源,并且我展示了一种更有效的方法来绕过错误。【参考方案3】:

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

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

【讨论】:

什么是崩溃类型? 'removeObjectFromSubitemsAtIndex' 不会删除这些子项,它们仍然存在于您的存储中,只是删除对象之间的关系的方式。 kItemsKey 是一个常量,只是为了方便 KVO 方法调用而添加的。这是您正在为其编写方法的有序关系的名称。 我认为是这样。谢谢。但是我的问题是使用这些方法没有将数据保存到数据库中。 !!!!!!!!!只需复制代码并更改方法名称,就可以完美运行!!!这是最快的答案。 这很棒,但是创建有序集的临时副本是不必要的。罪魁祸首是willChangeValueForKey:withSetMutation:usingObjects,您已成功避免。之后,只需根据需要使用[[self primitiveValueForKey:ChildrenKey] unionOrderedSet:values][[self primitiveValueForKey:ChildrenKey] minusOrderedSet:values]。详情见我的回答。【参考方案4】:

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

[managedObjectModel kc_generateOrderedSetAccessors];

特别是一个实体:

[managedObjectModel kc_generateOrderedSetAccessorsForEntity:entity];

或者只针对一种关系:

[managedObjectModel kc_generateOrderedSetAccessorsForRelationship:relationship];

【讨论】:

我想知道这是否会与 Apple 的真正修复冲突? 这不应该与苹果的修复冲突,因为它的目的是无论如何都要覆盖苹果的实现。当/如果 Apple 确实修复了这个问题,也许我会添加一个 - (BOOL)kc_needsOrderedSetAccessorFix; 或检查 Foundation/iOS 版本的东西。 CocoaPods 主仓库中已经有一个 KCOrderedAccessorFix.podspec。因此,为了将其链接到您的项目,您只需将“pod 'KCOrderedAccessorFix'”添加到您的 Podfile 这在 iOS 8 中存在一些问题(objc_msg_send 的方法签名不正确) 在 iOS9 上它可以工作,干得好!这是有史以来最好的解决方案,无需更改代码中的任何内容!【参考方案5】:

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

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

例如Core Data Release Notes for iOS v5.0 参考这个。

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

【讨论】:

无法轻易重构文字字符串。如果您使用代码,编译器可以键入检查 self.subitems。 @logancautrell 是的,这是正确的。这取决于特定用例的优先级。一般来说,我专注于节省资源,尤其是在这种情况下,因为这只是一种解决方法。 文字字符串可以替换为NSStringFromSelector(@selector(subitems)) :)【参考方案6】:

我已经跟踪了这个错误。它出现在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];

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

- (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 的方案要好得多。当您拥有大型数据集时,这会产生很大的开销。更简单的解决方案似乎只是初始版本的重构版本,其中未显示方法。 我仍然在 addChildren 中看到崩溃:*** 由于未捕获的异常“NSInvalidArgumentException”而终止应用程序,原因:“-[TrackHistory insertTrackpoints:atIndexes:]:无法识别的选择器发送到实例 0x1702b1b20” @OwenGodfrey 为了更简单的解决方案,您在哪里实施这些方法?我遇到了一个异常:[Parent insertObject:inChildrenAtIndex:] 无法识别的选择器发送到实例 0x6180000ac480。 您的变量是“父”,大写“P”?这是否意味着您将类称为“Parent”,或者您是否有一个名为“Parent”的实例变量?如果我的类是 Parent,那么我在 Parent 的底部实现了这个方法,但是你需要在一个实例上调用它,它更有可能被命名为“parent”,带有小写的“p”,因为这些不是类方法.【参考方案7】:

Apple docs To Many Relations 说:您应该使用

访问代理可变集或有序集
NSMutableOrderedSet * set = [managedObject mutableOrderedSetValueForKey:@"toManyRelation"];

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

【讨论】:

公平地说,文档还说:“或自动生成的关系变异器方法之一(请参阅动态生成的访问器方法):” 好吧好吧……你是对的。那么,假设这是最简单的工作方式......【参考方案8】:

收到同样的错误,@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 回答中一样。【参考方案9】:

如果你使用的是 mogenerator,那么不要

[parentObject add<Child>sObject:childObject];

简单地使用:

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

【讨论】:

因为 mogenerator 负责处理您原本必须编写的额外代码,并且只允许访问底层集合对象。 看起来刚刚提交了一个修复程序,这意味着 mogenerator 将生成更正的实体...github.com/dmakarenko/mogenerator/commit/… 我正在使用mogenerator,但我仍然有错误。【参考方案10】:

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

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

这消除了对以后可能与 Apple 解决错误后生成的代码发生冲突的类别的需求。

这还有一个额外的好处,那就是成为官方的方式!

【讨论】:

这给出了以下错误:'[ valueForUndefinedKey:]:此类不符合键子项的键值编码。' 虽然我仍然对这未在 Apple 的已知问题中列出的问题感到恼火(我已经为它显然是徒劳的姿态打开了雷达),但这个解决方案对我来说完美无缺。跨度> 希望我早点看到这个答案;我一直在使用投票最高的答案,直到我最近做了一些挖掘并最终实现了你在这里所拥有的:) 为什么addObject:被调用了两次?【参考方案11】:

似乎如果您通过将父级设置为子级而不是相反的方式将父级与子级链接起来,则它不会崩溃。

如果你这样做:

[child setParent:parent]

而不是

[parent setChildObects:child]

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

【讨论】:

当双方都是对多的时候没有多大好处。那么就没有明确的父子关系了。【参考方案12】:

我也遇到过同样的问题,但只是在我尝试了与我一直在做的不同的事情时。我看不到 subItem 的代码,但我会假设它有一个指向 item 的反向链接。让我们把这个尊敬的链接称为“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;

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

【讨论】:

这很好。它解决了整个问题并使其保持有序。仍然疯狂的是错误仍然存​​在。此答案的另一个好处是,如果您重新生成核心数据模型,则不必重写错误修复。谢谢! 查看我的其他答案。我更详细地跟踪了这个错误。这仍然是最简单的方法,但另一种方法是最好的,因为它开辟了更多的可能性。 哇!最后!!!谢谢! (尝试了您的其他代码,但出现错误,在 [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:indexSet forKey:ChildrenKey];) 【参考方案13】:

我刚刚遇到了这个问题,并使用比此处概述的其他实现更简单的实现来解决它。我只是利用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 子类之前使用的。

【讨论】:

【参考方案14】:

我在使用 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,你可以做同样的事情,因为这是我最初想到的地方:)

【讨论】:

【参考方案15】:

我同意这里可能存在错误。我已经修改了 add object >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 框架在雪豹上不可用。但是,是的,我没有看到这种失败,尽管我不确定这在性能上是最好的。我猜这需要整个集合并替换它,而不是仅仅插入所需的记录。【参考方案16】:

我认为每个人都忽略了真正的问题。它不在访问器方法中,而是在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 失败

看起来解决方法是更改​​集合运算符的实现,以便它们透明地处理类型。没有理由 -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

【讨论】:

【参考方案17】:

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

答案是不要在您的 NSManagedObject 子类中编写任何方法或其他内容。我认为这是一个编译器错误。很奇怪的bug..

希望对您有所帮助..

【讨论】:

那么,如果我无法实施其他修复,我应该如何解决这个问题?【参考方案18】:

我通过将逆设置为No Inverse解决了这个问题,我不知道为什么,可能是Apple Bug.

【讨论】:

【参考方案19】:

我有一个名为“信号”而不是“子项目”的项目。 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 项,因此性能不是什么大问题 - 但请指出任何相关内容。

谢谢,

达米安

【讨论】:

【参考方案20】:

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

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

查看我的模型后,我意识到我愚蠢地忘记选中“多对多关系”复选框。

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

【讨论】:

【参考方案21】:

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

[item addSubitemsObject:subItem];

用这个:

item.subitemsObject = subItem;

【讨论】:

【参考方案22】:

SWIFT 中正确答案的更好版本

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


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

【讨论】:

【参考方案23】:

我发现使用 LeeIII 的方法有效,但在分析时发现它非常慢。解析 1000 个项目需要 15 秒。注释掉添加关系的代码将 15 秒变为 2 秒。

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

@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;
    

我希望这对其他人有帮助!

【讨论】:

【参考方案24】:

罗伯特,

我同意您的回答对此有用,但请记住,已经有一种自动创建的方法可以将一整套值添加到关系中。 Apple 的文档(as seen here 在“对多关系”部分或here 在“自定义对多关系访问器方法”部分)以这种方式实现它们:

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

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

【讨论】:

【参考方案25】:

我很确定终于是fixed in iOS 10 beta 6!

【讨论】:

以上是关于NSOrderedSet 生成的访问器中抛出的异常的主要内容,如果未能解决你的问题,请参考以下文章

捕获 AuthenticationProvider 中抛出的异常

捕获在不同线程中抛出的异常

如何捕获 node_modules 中抛出的异常

如何捕获ctypes中抛出的异常?

是否可以捕获 JavaScript 异步回调中抛出的异常?

忽略在 C# .NET 中抛出的特定异常