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

手伝って頂けますか?

更新:

私のバグレポートからわずか1787日後の今日(2016年8月1日)に、Appleは次のように書いています。。これが適切な時期であることを願いましょう:)


5
同じ問題が発生しています。うまくいけば、すぐに修正されます。可変順序セットを直接使用することは、当面の簡単な回避策ですが。注:私はmogeneratorを使用していますが、生成されたコードのこの部分には同じAppleジェネレーターを内部で使用していると想定しています。
チャドポドスキ

12
もうすぐ2年!iOS 7、Appleで修正しますか?-このバグがまだ残っているかどうか疑問に思っている人と共有したいと思います。「はい、あります。」
an0 2013年

1
現在2年に非常に近いですが、これはすべてのxcode 5開発者プレビューで依然として問題です。
Korvin Szanto 2013

2
適切なKVCアクセサーを使用しても問題は解決しませんか?(つまりmutableOrderedSetValueForKey:
2014

3
Mavericksではまだ問題があるようです。
Tim

回答:


263

私はあなたのデータモデルと私の名前を別の名前で再現しました。どちらの場合も同じエラーが発生しました。

Appleの自動生成コードのバグのようです。


60
バグIDは10114310です。これは2011年9月13日に報告されましたが、今日(2012年1月15日)、まだ「オープン」です。同じ問題を抱えている人の数を考えると、それは驚くべきことです。
Dev

14
更新:今日(2012年5月11日)バグ#10114310は、レポートの241日後(2011年9月13日)にまだ開いています。信じられない。
Devの

23
私はこれをWWDCのCoreData Labセッションの1つでAppleエンジニアと一緒に持ち込みました。彼らは問題を認識し、それが本物のバグであること、そして私が見たところそれが「クリティカル」ステータスであることを認めますが、もちろん彼らがいつそれを修正するかについての約束はありません。iOS6 / Mountain Lionで修正されるとは思いません。このレーダーをさらに複製するといいと思います。現在、約25 dupですが、多いほど良いです!
DaGaMs

40
今日チェックしたところ、iOS 7 GM / OMGにはまだ残っています!信じられない…
an0

79
更新:バグ#10114310を埋めてから797日、2つの新しいメジャーiOSバージョン、および無数のXcodeバージョンが経過しました。そして、それはまだ「オープン」です。信じられない。
Dev

244

ここにバグがあることに同意します。NSMutableOrderedSetに正しく追加するように、addオブジェクトセッターの実装を変更しました。

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

セットをself.subitemsに再割り当てすると、Will / DidChangeValue通知が確実に送信されます。


あなたのコードスニペットは、この問題を回避するために必要なものでした。アップルが最終的に問題を修正することを願っていますが、今のところ私はあなたのアプローチの使用に関する問題を見ていません。
Christopher Hujanen

この回避策を実装しようとすると、このエラーが発生します。これに?
DerekH 2012年

@DerekH isEqualToSetはNSSetのみが持つメソッドであるため、NSManagedObjectに戻る前にポインタをNSArrayとして変換、作成、または処理していると思います。何らかの理由でisEqualToOrderedSetを呼び出して、セットが必要かどうかを判断する必要があります。変更するかそのままにしておく
InitJason 2012年

3
@MarkAmeryテスト済み。確認済み。動的セッターself.subitemsは通知を送信します。JLustソリューションは正しいです。
bernstein 2013

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メソッド呼び出しの便宜のために追加された定数です。これは、メソッドを記述している順序付けられた関係の名前です。
ドミトリーマカレンコ2012

1
それが私の考えです。ありがとう。しかし、私の問題は、これらの方法を使用してデータがデータベースに保存されないことです。
Bagusflyer 2012

4
!!!!!!!!! コードをコピーしてメソッド名を変更するだけで、完全に機能します!!! これが最速の答えです。
flypig 2013

1
これはすばらしいことですが、順序付けられたセットの一時的なコピーを作成する必要はありません。犯人はでwillChangeValueForKey:withSetMutation:usingObjects、これは回避できました。その後、[[self primitiveValueForKey:ChildrenKey] unionOrderedSet:values]または[[self primitiveValueForKey:ChildrenKey] minusOrderedSet:values]を適宜使用します。詳細については、私の回答を参照してください。
オーウェンゴッドフリー、

38

はい、これは間違いなくCore Dataのバグです。私はしばらく前にObjC-Runtimeベースの修正を書きましたが、そのときすぐに修正されると思いました。とにかく、そのような運はないので、KCOrderedAccessorFixとしてGitHubに投稿しました。すべてのエンティティの問題を回避します。

[managedObjectModel kc_generateOrderedSetAccessors];

特に1つのエンティティ:

[managedObjectModel kc_generateOrderedSetAccessorsForEntity:entity];

または、1つの関係についてのみ:

[managedObjectModel kc_generateOrderedSetAccessorsForRelationship:relationship];

これはAppleからの実際の修正と競合するのだろうか?
tia

3
これはAppleの修正と競合しないはずです。その目的は、Appleの実装をオーバーライドすることです。これが実際にAppleによって修正された場合は、- (BOOL)kc_needsOrderedSetAccessorFix;Foundation / iOSのバージョンをチェックするか何かを追加します。
スターリングアーチャー

2
CocoaPodsマスターリポジトリにはすでにKCOrderedAccessorFix.podspecがあります。これをプロジェクトにリンクするには、「pod 'KCOrderedAccessorFix'」をPodfileに追加するだけです
Anton Matosov

これにはiOS 8でいくつかの問題がありました(のメソッドシグネチャが正しくありませんobjc_msg_send
NSTJ 2014

iOS9ではうまくいきました。これはこれまでで最高のソリューションであり、コードを変更する必要はありません。
Borzh

32

コピーを作成する代わりに、NSObjectのアクセサを使用して関係のNSMutableOrderedSetにアクセスすることをお勧めします。

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

たとえば、iOS v5.0コアデータリリースノートはこれを参照します。

短いテストでは、私のアプリケーションで機能しました。


1
リテラル文字列を簡単にリファクタリングすることはできません。コードを使用する場合、コンパイラーはcheck self.subitemsと入力できます。
logancautrell、

1
@logancautrellはいこれは正しいです。特定のユースケースの優先度に依存します。これは単なる回避策だったので、一般に、特にこの場合はリソースの節約に焦点を当てています。
ステファン

2
文字列リテラルを置き換えてもよいNSStringFromSelector(@selector(subitems))けれども:)
ジャック

17

バグを追跡しました。で発生しwillChangeValueForKey:withSetMutation:usingObjects:ます。

この呼び出しにより、追跡が困難な一連の通知が発生します。もちろん、あるレスポンダへの変更は別のレスポンダに影響を与える可能性があります。これが、Appleが何もしなかった理由だと思います。

ただし、Setで問題はなく、OrderedSetでのSetオペレーションだけが誤動作します。つまり、変更が必要なメソッドは4つだけです。したがって、私が行ったのは、Set操作を同等のArray操作に変換することだけでした。これらは完全に機能し、最小限の(ただし必要な)オーバーヘッドです。

重要なレベルでは、このソリューションには1つの重大な欠陥があります。オブジェクトを追加していて、そのオブジェクトの1つが既に存在する場合は、追加されないか、順序付きリストの後ろに移動されます(どちらかわかりません)。どちらの場合でも、到達するまでにオブジェクトの期待される順序付けされたインデックスは、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
「 - [:atIndexes:TrackHistory insertTrackpoints:]:認識されていないセレクタがインスタンス0x1702b1b20に送られた」***キャッチされない例外により「NSInvalidArgumentException」にアプリを終了、理由:私はまだaddChildrenでクラッシュを見ている
ビクターボグダン

@OwenGodfreyより簡単な解決策として、これらのメソッドをどこに実装しますか?例外が発生します:[Parent insertObject:inChildrenAtIndex:]認識されないセレクターがインスタンス0x6180000ac480に送信されました。
ダルマジオ2015年

あなたの変数は大文字の「P」を持つ「親」ですか?それは、クラスを「親」と呼ぶことを意味しますか、それとも「親」という名前のインスタンス変数を持っていますか?私のクラスがParentの場合、このメソッドをParentの下部に実装しましたが、インスタンスで呼び出す必要があります。これらはクラスメソッドではないため、小文字の "p"を使用して "parent"と命名される可能性が高くなります。 。
オーウェンゴッドフリー

10

多くの関係へのAppleのドキュメントは言う:プロキシ変更可能なセットにアクセスする必要がありますか、使用して順序集合します

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

このセットを変更すると、管理オブジェクトへの関係が追加または削除されます。アクセサを使用して、[]または。表記が間違っていて失敗します。


3
公平を期すために、ドキュメントも言う:「自動的に生成された関係のミューテータメソッド(動的に生成されるアクセサメソッドを参照)または1:」
マット

大丈夫大丈夫...あなたは正しい。それでは、それが最も簡単な方法であるとしましょう...
Nicolas Manzini 2014年

9

同じエラーが発生しましたが、@ 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

このコードをカテゴリに移動するのは良い点です。ただし、@ Dmitry Makarenkoの回答のように、will / setPrimitiveValue / didChange呼び出しで実際の追加/削除を受け入れる必要があります。
Vladimir Shutyuk 2013年

8

あなたがmogeneratorを使用している場合、代わりに

[parentObject add<Child>sObject:childObject];

単に使用:

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

mogeneratorが追加のコードを処理するため、そうしないと記述しなければならず、基本となるセットオブジェクトへのアクセスを許可するだけです。
Καrτhικ

修正プログラムはちょうど手段がmogeneratorが修正遺体...生成することを約束されているように見えますgithub.com/dmakarenko/mogenerator/commit/...
コンビナトリアル・

1
使用してmogeneratorいますが、まだバグがあります。
コーラス2013年

7

個人的には、@ Stephanによる別のソリューションで概説されているように、CoreDataで生成されたメソッドへの呼び出しをメソッドへの直接呼び出しに置き換えました:

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

これにより、バグが修正されたときに、Appleから生成されたコードのソリューションと後で競合する可能性があるカテゴリが不要になります。

これには、公式の方法であるというプラスの利点があります!


これにより、次のエラーが発生します: '[<CLASS 0x20886d10> valueForUndefinedKey:]:このクラスは、キーサブアイテムのコーディングに準拠したキー値ではありません。
jmstone617 2012年

これがAppleの既知の問題にリストされていないことに終わりはありません(明らかに無駄なジェスチャーのためにレーダーを開きました)が、このソリューションは完璧に機能しました。
Scott Corscadden 2013

私が以前にこの答えを見たことを願っています。私は最近、いくつかは、掘って、最終的にあなたがここに持っている、まさに実現しなかったまで、私は最高の投票答えを使用していた:)
ジャック

なぜaddObject:2回呼び出されるのですか?
Jason Moore

5

親を子に設定して親を子にリンクすると、クラッシュせずに逆に動作しないようです。

だからあなたがするなら:

[child setParent:parent]

の代わりに

[parent setChildObects:child]

少なくともiOS 7では機能し、関係に問題はありませんでした。


1
両方が多すぎる場合、あまり効果はありません。次に、明確な親子関係はありません。
fatuhoku 2014年

3

私は同じ問題を抱えていましたが、自分がやっていたこととは異なる何かを試したときだけです。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;

その効果は、アップル自身のコードを利用し、シンプルでクリーンなことです。さらに、セットが自動的に追加され、すべてのオブザーバーが更新されます。問題ない。


これはすごく素敵。それは問題全体を解決し、注文したままにします。バグがまだ存在していることはまだ狂気です。この回答のも​​う1つの利点は、コアデータモデルを再生成する場合、バグ修正を書き直す必要がないことです。ありがとう!
Johan S

私の他の答えを見てください。バグをより詳細に追跡しました。これはまだ最も簡単な方法ですが、より多くの可能性を開くため、他の方法が最適です。
オーウェンゴッドフリー、

うわー!最終的に!!!ありがとう!(他のコードを試してみましたが、エラーが発生しました。その間違ったタイプに関する何かが[self didChange:NSKeyValueChangeInsertion valuesAtIndexes:indexSet forKey:ChildrenKey];に送信されました。)
Leonard Pauli 14

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

この問題は、Objective-CからXCode 7搭載したSwift 2にプロジェクトを移行しているときに発生しました。そのプロジェクトは以前は機能していましたが、それには十分な理由があります。私は、このバグを修正するための代替メソッドを持つMOGeneratorを使用していました。ただし、すべての方法で交換が必要なわけではありません。

したがって、ここでは、可能な限りデフォルトのアクセサに依存する、サンプルクラスを使用した完全なソリューションを示します。

注文したアイテムのリストがあるとしましょう

1 対多の関係がある場合は、まず簡単に勝ちます。最も簡単なのは、次のようにすることです。

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

ここにバグがあると思う。NSMutableOrderedSetに正しく追加するために、addオブジェクトのセッターの実装を変更しました。

- (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 2012年

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

修正は、セット演算子の実装を変更して、型を透過的に処理することのようです。が-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 16年

2

この問題を解決するには、インバースをNo Inverseに設定しました。理由はわかりません。たぶん、Appleのバグがあるのか​​もしれません。ここに画像の説明を入力してください


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

ロバート、

私はあなたの答えがこれでうまくいくことに同意しますが、値のセット全体を関係に追加するための自動的に作成されたメソッドがすでにあることに注意してください。(Appleのドキュメントここに見られるように、「対多の関係」セクションの下、またはここに実装し、それらをこのように「カスタム対多の関係アクセサメソッド」セクションの下):

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

コアデータの外部でリレーションシップのセットを簡単にコンパイルし、このメソッドを使用してそれらを一度にすべて追加できます。それはあなたが提案した方法よりも醜くないかもしれません;)


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.