In NSOrderedSet ausgelöste Ausnahme generiert Accessoren

364

In meiner Lion-App habe ich dieses Datenmodell:

Geben Sie hier die Bildbeschreibung ein

Die Beziehung im subitemsInneren Item ist geordnet .

Xcode 4.1 (Build 4B110) hat sich für mich die Datei erstellt Item.h, Item.m, SubItem.hund SubItem.h.

Hier ist der Inhalt (automatisch generiert) von 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

Und hier ist der Inhalt (automatisch generiert) von Item.m:

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

@implementation Item

@dynamic name;
@dynamic subitems;

@end

Wie Sie sehen können, Itembietet die Klasse eine Methode namens addSubitemsObject:. Leider, wenn Sie versuchen, es auf diese Weise zu verwenden:

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

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

[item addSubitemsObject:subItem];

Dieser Fehler wird angezeigt:

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

Können Sie mir helfen?

Aktualisieren:

Nach nur 1.787 Tagen nach meinem Fehlerbericht schrieb mir Apple heute (1. August 2016) Folgendes : "Bitte überprüfen Sie dieses Problem mit der neuesten Beta-Version von iOS 10 und aktualisieren Sie Ihren Fehlerbericht unter bugreport.apple.com mit Ihren Ergebnissen." . Hoffen wir, dass dies der richtige Zeitpunkt ist :)

Dev
quelle
5
Ich sehe das gleiche Problem. Hoffentlich wird es bald behoben. Obwohl die direkte Verwendung des veränderlichen geordneten Satzes vorerst eine einfache Problemumgehung ist. Hinweis: Ich verwende Mogenerator, gehe jedoch davon aus, dass für diesen Teil des generierten Codes intern derselbe Apple-Generator verwendet wird.
Chad Podoski
12
Es ist fast 2 Jahre! Beheben Sie das Problem in iOS 7, Apple? - Ich möchte nur mit denen teilen, die sich fragen, ob dieser Fehler noch vorhanden ist: "Ja, das ist es."
an0
1
Fast zwei Jahre später ist dies immer noch ein Problem in allen xcode 5-Entwicklervorschauen.
Korvin Szanto
2
Sehen Sie das Problem immer noch, wenn Sie den entsprechenden KVC-Accessor verwenden? (dh mutableOrderedSetValueForKey:)
quellish
3
Scheint immer noch ein Problem bei Mavericks zu sein.
Tim

Antworten:

263

Ich habe Ihr Setup sowohl mit Ihrem Datenmodell als auch mit einem meiner eigenen mit unterschiedlichen Namen reproduziert. Ich habe in beiden Fällen den gleichen Fehler erhalten.

Sieht aus wie ein Fehler in Apples automatisch generiertem Code.

TechZen
quelle
60
Die Fehler-ID lautet 10114310. Sie wurde am 13. September 2011 gemeldet, ist aber heute (15. Januar 2012) noch "offen". Es ist unglaublich, wenn man bedenkt, wie viele Menschen das gleiche Problem haben.
Dev
14
Update: Heute (11. Mai 2012) ist der Fehler # 10114310 noch offen, 241 Tage nach meinem Bericht (13. September 2011). Nicht zu fassen.
Dev
23
Ich habe dies gerade mit einem Apple-Ingenieur während einer der CoreData Lab-Sitzungen bei WWDC besprochen. Sie erkennen das Problem an und dass es ein echter Fehler ist, und nach dem, was ich gesehen habe, hat es den "kritischen" Status, aber natürlich gibt es kein Versprechen, wann sie es beheben werden. Ich glaube nicht, dass es in iOS6 / Mountain Lion behoben wird. Ich denke, es wäre gut, dieses Radar weiter zu duplizieren. Derzeit hat es ungefähr 25 Dup's, je mehr desto besser!
DaGaMs
40
Gerade heute überprüft, ist es immer noch in iOS 7 GM / OMG da! Ich kann es nicht glauben ...
an0
79
Update: 797 Tage, 2 neue Hauptversionen für iOS und unzählige Xcode-Versionen sind vergangen, seit ich den Fehler # 10114310 behoben habe. Und es ist immer noch "offen". Nicht zu fassen.
Dev
244

Ich bin damit einverstanden, dass hier ein Fehler vorliegt. Ich habe die Implementierung des Add Object Setters so geändert, dass sie korrekt an ein NSMutableOrderedSet angehängt wird.

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

Durch erneutes Zuweisen des Satzes zu self.subitems wird sichergestellt, dass die Will / DidChangeValue-Benachrichtigungen gesendet werden.

InitJason
quelle
Ihr Code-Snippet war genau das, was ich brauchte, um dieses Problem zu umgehen. Ich hoffe, Apple behebt das Problem irgendwann, aber bisher habe ich keine Probleme mit der Verwendung Ihres Ansatzes gesehen.
Christopher Hujanen
Ich erhalte diese Fehlermeldung, wenn ich versuche, diese Problemumgehung zu implementieren das sehr gut finden?
DerekH
@DerekH isEqualToSet ist eine Methode, die nur NSSet hat. Ich vermute also, dass Sie einen Zeiger konvertiert, erstellt oder als NSArray behandelt haben, bevor Sie ihn an NSManagedObject zurückgeben. Dies sollte in jedem Fall der Aufruf von isEqualToOrderedSet sein, um festzustellen, ob der Satz benötigt wird sich zu ändern oder so zu bleiben, wie es ist.
InitJason
3
@ MarkAmery getestet. Verifiziert. Der dynamische Setter self.subitems sendet die Benachrichtigungen. Die JLust-Lösung ist also korrekt.
bernstein
3
Dies ist eine gute Antwort, aber ineffizient. Sie kopieren den gesamten bestellten Satz, ändern ihn und kopieren ihn dann zurück. Der Effekt ist nicht nur ein Treffer für das bestellte Set, sondern es werden Benachrichtigungen gesendet, dass bei jeder Änderung des bestellten Sets der gesamte Inhalt geändert wird! Wenn dieser geordnete Satz beispielsweise für eine UITable verwendet wird, kann dies schwerwiegende Auswirkungen auf die Aktualisierung haben. Ich habe in meiner Lösung genau beschrieben, woher der Fehler kommt, und ich habe eine effizientere Methode zur Umgehung des Fehlers gezeigt.
Owen Godfrey
111

Ich habe beschlossen, die Lösung durch Implementierung aller erforderlichen Methoden zu verbessern:

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];
    }
}
Dmitry Makarenko
quelle
1
Was ist der Absturztyp? 'removeObjectFromSubitemsAtIndex' löscht diese Unterelemente nicht, sie sind noch in Ihrem Speicher vorhanden. Auf diese Weise können Sie lediglich die Beziehung zwischen den Objekten entfernen.
Dmitry Makarenko
2
kItemsKey ist eine Konstante, die nur zur Vereinfachung bei KVO-Methodenaufrufen hinzugefügt wurde. Es ist ein Name der geordneten Beziehung, für die Sie Ihre Methoden schreiben.
Dmitry Makarenko
1
Das denke ich auch. Vielen Dank. Mein Problem ist jedoch, dass die Daten mit diesen Methoden nicht in der Datenbank gespeichert werden.
Bagusflyer
4
!!!!!!!!! Einfach den Code kopieren und die Methodennamen ändern, es funktioniert perfekt !!! Dies ist die schnellste Antwort.
Flypig
1
Das ist großartig, aber das Erstellen der temporären Kopie des bestellten Sets ist nicht erforderlich. Der Schuldige ist willChangeValueForKey:withSetMutation:usingObjects, was Sie erfolgreich vermieden haben. Danach einfach [[self primitiveValueForKey:ChildrenKey] unionOrderedSet:values]oder [[self primitiveValueForKey:ChildrenKey] minusOrderedSet:values]nach Bedarf verwenden. Siehe meine Antwort für Details.
Owen Godfrey
38

Ja, dies ist definitiv ein Core Data-Fehler. Ich habe vor einiger Zeit einen ObjC-Runtime-basierten Fix geschrieben, aber zu der Zeit dachte ich, dass er bald behoben sein würde. Wie auch immer, kein solches Glück, also habe ich es auf GitHub als KCOrderedAccessorFix gepostet . Umgehen Sie das Problem bei allen Ihren Entitäten:

[managedObjectModel kc_generateOrderedSetAccessors];

Eine Einheit im Besonderen:

[managedObjectModel kc_generateOrderedSetAccessorsForEntity:entity];

Oder nur für eine Beziehung:

[managedObjectModel kc_generateOrderedSetAccessorsForRelationship:relationship];
Sterling Archer
quelle
Ich frage mich, ob dies mit dem tatsächlichen Fix von Apple in Konflikt steht oder nicht.
Tia
3
Dies sollte nicht im Widerspruch zu Apples Fix stehen, da der Zweck darin besteht, die Implementierung von Apple zu überschreiben, egal was passiert. Wenn / wenn dies tatsächlich von Apple behoben wird, füge ich möglicherweise eine - (BOOL)kc_needsOrderedSetAccessorFix;oder etwas hinzu, das die Foundation / iOS-Version überprüft.
Sterling Archer
2
Im CocoaPods-Master-Repo befindet sich bereits eine KCOrderedAccessorFix.podspec. Um dies mit Ihren Projekten zu verknüpfen, können Sie einfach "pod 'KCOrderedAccessorFix'" zu Ihrem Podfile hinzufügen
Anton Matosov
Dies hatte einige Probleme mit iOS 8 (falsche Methodensignaturen für objc_msg_send)
NSTJ
Unter iOS9 funktioniert es, gute Arbeit! Dies ist die beste Lösung aller Zeiten, Sie müssen nichts an Ihrem Code ändern!
Borzh
32

Anstatt eine Kopie zu erstellen, schlage ich vor, den Accessor in NSObject zu verwenden, um Zugriff auf das NSMutableOrderedSet der Beziehungen zu erhalten.

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

zB die Hierzu beziehen sich Core Data Release Notes für iOS v5.0 .

In einem kurzen Test hat es in meiner Anwendung funktioniert.

Stephan
quelle
1
Literale Zeichenfolgen können nicht so einfach umgestaltet werden. Der Compiler kann check self.subitems eingeben, wenn Sie Code verwenden.
Logancautrell
1
@logancautrell ja das ist richtig. Dies hängt von der Priorität des jeweiligen Anwendungsfalls ab. Im Allgemeinen konzentriere ich mich darauf, Ressourcen zu sparen, insbesondere in diesem Fall, da dies nur eine Problemumgehung war.
Stephan
2
Die wörtliche Zeichenfolge kann jedoch durch ersetzt werden NSStringFromSelector(@selector(subitems)):)
Ja͢ck
17

Ich habe den Fehler verfolgt. Es kommt in vorwillChangeValueForKey:withSetMutation:usingObjects: .

Dieser Aufruf löst eine Reihe von Benachrichtigungen aus, die möglicherweise schwer zu verfolgen sind, und natürlich können Änderungen an einem Antwortenden Auswirkungen auf einen anderen haben. Ich vermute, dass Apple deshalb nichts unternommen hat.

Es ist jedoch in Set in Ordnung und es sind nur die Set-Operationen auf einem OrderedSet, die nicht funktionieren. Das heißt, es gibt nur vier Methoden, die geändert werden müssen. Daher habe ich nur die Set-Operationen in ihre entsprechenden Array-Operationen konvertiert. Diese funktionieren perfekt und minimale (aber notwendige) Gemeinkosten.

Auf einer kritischen Ebene weist diese Lösung einen kritischen Fehler auf. Wenn Sie Objekte hinzufügen und eines der Objekte bereits vorhanden ist, wird es entweder nicht hinzugefügt oder an den Ende der geordneten Liste verschoben (ich weiß nicht, welches). In beiden Fällen unterscheidet sich der erwartete geordnete Index des Objekts zum Zeitpunkt unserer Ankunft didChangevon dem erwarteten. Dies kann die Apps einiger Leute beschädigen, hat jedoch keine Auswirkungen auf meine, da ich immer nur neue Objekte hinzufüge oder deren endgültige Position bestätige, bevor ich sie hinzufüge.

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

Natürlich gibt es eine einfachere Lösung. es ist wie folgt;

- (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];
    }]];
}
Owen Godfrey
quelle
Schade, dass alle anderen diese Antwort übersehen haben, scheint definitiv die beste Lösung zu sein.
George
Diese Lösung bietet eine viel bessere Leistung als diejenigen, die orderSetWithOrderedSet verwenden, um einen lokalen Satz zu erstellen. Das hat einen großen Overhead, wenn Sie große Datenmengen haben. Die einfachere Lösung scheint nur eine überarbeitete Version der ursprünglichen mit den nicht gezeigten Methoden zu sein.
David Pettigrew
1
Ich sehe immer noch einen Absturz in addChildren: *** Beenden der App aufgrund der nicht erfassten Ausnahme 'NSInvalidArgumentException', Grund: '- [TrackHistory insertTrackpoints: atIndexes:]: Nicht erkannter Selektor an Instanz 0x1702b1b20 gesendet'
Victor Bogdan
@OwenGodfrey Um die einfachere Lösung zu finden, wo implementieren Sie diese Methoden? Ich erhalte eine Ausnahme: [Parent insertObject: inChildrenAtIndex:] Nicht erkannter Selektor, der an die Instanz 0x6180000ac480 gesendet wurde.
Dalmazio
Ihre Variable ist "Parent" mit einem Großbuchstaben "P"? Bedeutet das, dass Sie die Klasse "Parent" aufrufen oder haben Sie eine Instanzvariable mit dem Namen "Parent"? Wenn meine Klasse Parent ist, habe ich diese Methoden am Ende von Parent implementiert, aber Sie müssten sie für eine Instanz aufrufen, die eher "parent" mit einem Kleinbuchstaben "p" heißt, da dies keine Klassenmethoden sind .
Owen Godfrey
10

In den Apple-Dokumenten zu vielen Beziehungen heißt es: Sie sollten mit auf das veränderbare Proxy-Set oder das geordnete Set zugreifen

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

Durch Ändern dieses Satzes werden Beziehungen zu Ihrem verwalteten Objekt hinzugefügt oder entfernt. Zugriff auf die veränderbare geordnete Menge mit dem Accessor, ob mit [] oder. Notation ist falsch und wird fehlschlagen.

Nicolas Manzini
quelle
3
Um fair zu sein, sagen die Dokumente auch: "oder eine der automatisch generierten Beziehungsmutator-Methoden (siehe Dynamisch generierte Accessor-Methoden):"
Matt
Okay, okay ... du hast recht. Dann nehmen wir an, das ist die einfachste Arbeitsweise ...
Nicolas Manzini
9

Erhielt den gleichen Fehler, @ LeeIII-Lösung funktionierte für mich (danke!). Ich schlage vor, es leicht zu ändern:

  • Verwenden Sie die Kategorie objectiv-c, um die neue Methode zu speichern (damit wir unsere Methode nicht verlieren, wenn Item erneut generiert wird).
  • Überprüfen Sie, ob wir bereits einen veränderlichen Satz haben

Inhalt von 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
Danik
quelle
Guter Punkt, um diesen Code in eine Kategorie zu verschieben. Trotzdem müssen wir das tatsächliche Hinzufügen / Entfernen mit will / setPrimitiveValue / didChange-Aufrufen wie in der Antwort von @Dmitry Makarenko berücksichtigen.
Vladimir Shutyuk
8

Wenn Sie Mogenerator verwenden, dann anstelle von

[parentObject add<Child>sObject:childObject];

Verwenden Sie einfach:

[[parent object <child>sSet] addObject:childObject];
Καrτhικ
quelle
Da mogenerator sich um den zusätzlichen Code kümmert, den Sie sonst schreiben müssten, und einfach den Zugriff auf das zugrunde liegende Set-Objekt ermöglicht.
Καrτhικ
Es sieht so aus, als ob gerade ein Fix begangen wurde, der bedeutet, dass Mogenerator korrigierte Körper erzeugt ... github.com/dmakarenko/mogenerator/commit/…
kombinatorisch
1
Ich benutze, mogeneratoraber ich habe immer noch den Fehler.
Colas
7

Persönlich habe ich gerade die Aufrufe der von CoreData generierten Methoden durch direkte Aufrufe der Methode ersetzt, wie in einer anderen Lösung von @Stephan beschrieben:

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

Dadurch werden keine Kategorien mehr benötigt, die später mit einer Lösung von Apple für den generierten Code in Konflikt geraten könnten, wenn der Fehler behoben ist.

Dies hat das zusätzliche Plus, der offizielle Weg zu sein!

Grouchal
quelle
Dies gibt den folgenden Fehler aus: '[<CLASS 0x20886d10> valueForUndefinedKey:]: Diese Klasse ist für die Schlüsselunterelemente nicht mit der Schlüsselwertcodierung kompatibel.'
jmstone617
Obwohl es mich immer noch ärgert, dass dies nicht in den bekannten Problemen von Apple aufgeführt ist (ich habe ein Radar für die scheinbar vergebliche Geste geöffnet, die es ist), hat diese Lösung für mich einwandfrei funktioniert.
Scott Corscadden
Ich wünschte, ich hätte diese Antwort früher gesehen. Ich habe die Antwort mit der höchsten Stimme verwendet, bis ich kürzlich ein
bisschen
Warum wird addObject:zweimal angerufen?
Jason Moore
5

Es scheint, dass, wenn Sie das Elternteil mit dem Kind verknüpfen, indem Sie das Elternteil auf das Kind setzen und nicht umgekehrt, es ohne Absturz funktioniert.

Wenn Sie dies tun:

[child setParent:parent]

anstatt

[parent setChildObects:child]

Es sollte funktionieren, zumindest funktioniert es unter iOS 7 und hatte keine Probleme mit der Beziehung.

Cata
quelle
1
Tut nicht viel, wenn beide Seiten zu viele sind. Dann gibt es keine klare Eltern-Kind-Beziehung.
Fatuhoku
3

Ich hatte das gleiche Problem, aber nur, wenn ich etwas anderes ausprobierte als das, was ich getan hatte. Ich kann den Code für subItem nicht sehen, aber ich gehe davon aus, dass er einen umgekehrten Link zum Element hat. Nennen wir diesen Reveres-Link "parentItem". Die einfachste Lösung ist folgende:

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;

Der Effekt ist, dass es den eigenen Code von Apple verwendet und einfach und sauber ist. Außerdem wird das Set automatisch hinzugefügt und alle Beobachter werden aktualisiert. Kein Problem.

Owen Godfrey
quelle
Das ist sehr nett. Es löst das ganze Problem und hält es auch geordnet. Immer noch verrückt, dass der Fehler immer noch vorhanden ist. Ein weiterer Vorteil dieser Antwort ist, dass Sie Ihre Fehlerbehebungen nicht neu schreiben müssen, wenn Sie Ihre Kerndatenmodelle neu generieren. Vielen Dank!
Johan S
Siehe meine andere Antwort. Ich habe den Fehler genauer verfolgt. Dies ist immer noch der einfachste Weg, aber die andere Methode ist die beste, weil sie mehr Möglichkeiten eröffnet.
Owen Godfrey
Beeindruckend! Schließlich!!! Vielen Dank! (Versuchte Ihren anderen Code, bekam aber Fehler, etwas über diesen falschen Typ wurde an [self didChange: NSKeyValueChangeInsertion valuesAtIndexes: indexSet forKey: ChildrenKey] gesendet;)
Leonard Pauli
3

Ich habe dieses Problem einfach nicht verstanden und es mit einer viel einfacheren Implementierung als den anderen hier beschriebenen gelöst. Ich benutze einfach die verfügbaren Methoden NSManagedObjectfür den Umgang mit Beziehungen, wenn ich keine Unterklassen benutze.

Eine Beispielimplementierung zum Einfügen einer Entität in eine NSOrderedSetBeziehung würde folgendermaßen aussehen:

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

Dies funktioniert perfekt und wurde von mir verwendet, bevor ich zu NSManagedObjectUnterklassen gewechselt bin .

Mic Pringle
quelle
3

Dieses Problem trat mir bei der Migration eines Projekts von Objective-C auf Swift 2 mit XCode 7 auf . Dieses Projekt hat früher funktioniert, und das aus gutem Grund: Ich habe MOGenerator verwendet, der Ersatzmethoden zur Behebung dieses Fehlers hatte. Nicht alle Methoden erfordern jedoch einen Austausch.

Hier ist die vollständige Lösung mit einer Beispielklasse, die sich so weit wie möglich auf Standard-Accessoren stützt.

Nehmen wir an, wir haben eine Liste mit bestellten Artikeln

Zuerst ein schneller Gewinn, wenn Sie eine Eins-zu-Viele-Beziehung haben. Am einfachsten ist es, einfach Folgendes zu tun:

item.list = list

anstatt

list.addItemsObject(item)

Nun, wenn das keine Option ist , ist hier, was Sie tun können:

// 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

Wenn Sie Objective-C verwenden, können Sie natürlich genau das Gleiche tun, da ich hier überhaupt auf die Idee gekommen bin :)

Nycen
quelle
3

Ich bin damit einverstanden, dass es hier vielleicht einen Fehler gibt. Ich habe die Implementierung von add object> setter so geändert, dass sie korrekt an ein NSMutableOrderedSet angehängt wird.

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

Durch erneutes Zuweisen des Satzes zu self.subitems wird sichergestellt, dass die Will / DidChangeValue-Benachrichtigungen> gesendet werden.

Leelll, sind Sie sicher, dass nach einer solchen benutzerdefinierten Einrichtung der in diesem Satz gespeicherten NSMutableOrderedSet-Werte von CoreData korrekt in der Datenbank gespeichert werden? Ich habe das nicht überprüft, aber es sieht so aus, als ob CoreData nichts über NSOrderedSet weiß und NSSet als zu viele Beziehungscontainer erwartet.

DisableR
quelle
Damit CoreData ein NSOrderedSet-Objekt zurückgeben oder annehmen kann, müssen mehrere Bedingungen erfüllt sein, wie diese gestartete Frage gezeigt hat. Die häufigsten Fehler, die ich sehe, wenn Leute, die meinen Code teilen, Entwickler waren, die Lion nicht ausführen. Das NSOrderedSets-Framework ist auf snowleopard nicht verfügbar. Aber ja, ich habe nicht gesehen, dass dies fehlschlägt, obwohl ich nicht sicher bin, ob dies die beste Leistung ist. Ich würde vermuten, dass dies den gesamten Satz übernimmt und ersetzt, anstatt nur den gewünschten Datensatz einzufügen.
InitJason
2

Ich denke, jeder vermisst das eigentliche Problem. Es liegt nicht in den Accessor-Methoden, sondern in der Tatsache, dass NSOrderedSetes sich nicht um eine Unterklasse von handelt NSSet. Wenn -interSectsSet:also eine geordnete Menge als Argument aufgerufen wird, schlägt dies fehl.

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

 [setB intersectsSet:setA];

scheitert mit *** -[NSSet intersectsSet:]: set argument is not an NSSet

Es sieht so aus, als ob das Update darin besteht, die Implementierung der Set-Operatoren so zu ändern, dass sie die Typen transparent behandeln. Kein Grund warum a-intersectsSet: mit einem bestellten oder ungeordneten Set arbeiten sollte.

Die Ausnahme tritt in der Änderungsbenachrichtigung auf. Vermutlich in dem Code, der die umgekehrte Beziehung behandelt. Da passiert es nur, wenn ich eine umgekehrte Beziehung setze.

Das Folgende hat den Trick für mich getan

@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
Entropie
quelle
2

Ich habe gerade das Problem in Swift (Xcode 6.1.1) bekommen.

Die Antwort lautete: KEINE METHODE ODER ZUSÄTZLICHE DINGE CODE in Ihren NSManagedObject-Unterklassen codieren. Ich denke, es ist ein Kompilatorfehler. Sehr seltsamer Fehler ..

Ich hoffe es hilft ..

Lobodart
quelle
3
Was kann ich tun, um die anderen Korrekturen zu beheben, wenn ich sie nicht implementieren kann?
Ben Leggiero
2

Ich habe dieses Problem gelöst, indem ich das Inverse auf No Inverse gesetzt habe. Ich weiß nicht warum. Vielleicht gibt es einen Apple Bug.Geben Sie hier die Bildbeschreibung ein

LevinYan
quelle
1

Ich habe die gleiche Situation mit einem Element namens "Signale" anstelle von "Unterelementen". Die Lösung mit Tempset funktioniert in meinen Tests. Außerdem hatte ich ein Problem mit der Methode removeSignals :. Diese Überschreibung scheint zu funktionieren:

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

Wenn es einen besseren Weg gibt, lassen Sie es mich bitte wissen. Meine Werteingabe beträgt nie mehr als 10 bis 20 Elemente, daher ist die Leistung kein großes Problem. Bitte weisen Sie jedoch auf relevante Informationen hin.

Vielen Dank,

Damien

Damien Del Russo
quelle
1

Ich habe diese Frage gefunden, indem ich nach der Fehlermeldung gegoogelt habe, und wollte nur darauf hinweisen, dass ich auf etwas andere Weise auf diesen Fehler gestoßen bin (ohne geordnete Sätze zu verwenden). Dies ist keine echte Antwort auf die gegebene Frage, aber ich poste sie hier, nur für den Fall, dass sie für alle anderen hilfreich ist, die bei der Suche auf diese Frage stoßen.

Ich habe eine neue Modellversion hinzugefügt, einige Beziehungen zu vorhandenen Modellen hinzugefügt und die add * Object-Methoden in der Header-Datei selbst definiert. Als ich versuchte, sie anzurufen, bekam ich den obigen Fehler.

Nachdem ich meine Modelle überprüft hatte, stellte ich fest, dass ich dummerweise vergessen hatte, das Kontrollkästchen "To-Many Relationship" zu aktivieren.

Wenn Sie also darauf stoßen und keine bestellten Sets verwenden, überprüfen Sie Ihr Modell.

BenV
quelle
1

Ich habe eine Lösung für diesen Fehler gefunden, die für mich funktioniert. Ich ersetze dies einfach:

[item addSubitemsObject:subItem];

mit diesem:

item.subitemsObject = subItem;
Bimawa
quelle
1

Bessere Version der richtigen Antwort in SWIFT

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

tempSet.add(newItem)
parent!.subItems = tempSet
emreoktem
quelle
0

Ich fand, dass die Verwendung der Methode von LeeIII funktioniert hat, aber bei der Profilerstellung stellte sich heraus, dass sie drastisch langsam war. Das Parsen von 1000 Elementen dauerte 15 Sekunden. Durch Auskommentieren des Codes zum Hinzufügen der Beziehung wurden 15 Sekunden zu 2 Sekunden.

Meine Problemumgehung (die schneller, aber viel hässlicher ist) besteht darin, ein temporäres veränderbares Array zu erstellen und es dann in die geordnete Menge zu kopieren, wenn das gesamte Parsen abgeschlossen ist. (Dies ist nur ein Leistungsgewinn, wenn Sie viele Beziehungen hinzufügen möchten).

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

Ich hoffe das hilft jemand anderem!

Robert
quelle
0

Robert,

Ich bin damit einverstanden, dass Ihre Antwort dafür funktioniert, aber denken Sie daran, dass es bereits eine automatisch erstellte Methode gibt, um einer Beziehung eine ganze Reihe von Werten hinzuzufügen. Die Dokumentation von Apple ( hier im Abschnitt "Zu viele Beziehungen" oder hier im Abschnitt "Benutzerdefinierte Methoden für den Zugriff auf viele Beziehungen") implementiert sie folgendermaßen:

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

Mit dieser Methode können Sie Ihre Beziehungen problemlos außerhalb der Kerndaten kompilieren und dann alle auf einmal hinzufügen. Es könnte weniger hässlich sein als die von Ihnen vorgeschlagene Methode;)

JiuJitsuCoder
quelle