webentwicklung-frage-antwort-db.com.de

In NSOrderedSet generierte Accessoren ausgelöste Ausnahme

In meiner Lion-App habe ich folgendes Datenmodell:

enter image description here

Die Beziehung subitems innerhalb Itemist geordnet.

Xcode 4.1 (Build 4B110) hat für mich die Dateien Item.h, Item.m, SubItem.h Und SubItem.h Erstellt.

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, bietet die Klasse Item eine Methode mit dem Namen 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 erscheint:

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

Können Sie mir helfen?

pdate:

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

363
Dev

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

Sieht aus wie ein Fehler in Apples automatisch generiertem Code.

263
TechZen

Ich bin damit einverstanden, dass es hier einen Fehler gibt. 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 die Neuzuweisung des Satzes zu "self.subitems" wird sichergestellt, dass die Benachrichtigungen "Will/DidChangeValue" gesendet werden.

244
InitJason

Ich habe beschlossen, die Lösung zu verbessern, indem ich alle erforderlichen Methoden implementiere:

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

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

[managedObjectModel kc_generateOrderedSetAccessors];

Eine Einheit im Besonderen:

[managedObjectModel kc_generateOrderedSetAccessorsForEntity:entity];

Oder nur für eine Beziehung:

[managedObjectModel kc_generateOrderedSetAccessorsForRelationship:relationship];
38
Sterling Archer

Statt eine Kopie zu erstellen, empfehle ich, 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];
 }

z.B. die Core Data Release Notes für iOS v5. beziehen sich darauf.

In einem kurzen Test hat es in meiner Bewerbung geklappt.

32
Stephan

Ich habe den Bug aufgespürt. Es kommt vor in willChangeValueForKey:withSetMutation:usingObjects:.

Dieser Aufruf löst eine Reihe von Benachrichtigungen aus, die möglicherweise schwer nachzuverfolgen sind, und natürlich können Änderungen an einem Antwortenden Auswirkungen auf einen anderen haben. Ich vermute, aus diesem Grund haben Apple nichts getan.

In Set ist dies jedoch in Ordnung, und es sind nur die Set-Vorgänge für ein OrderedSet, bei denen eine Fehlfunktion auftritt. Das heißt, es gibt nur vier Methoden, die geändert werden müssen. Daher habe ich nur die Set-Operationen in die entsprechenden Array-Operationen konvertiert. Diese arbeiten 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 in den hinteren Bereich der geordneten Liste verschoben (ich weiß nicht, welches). In beiden Fällen unterscheidet sich der erwartete geordnete Index des Objekts zum Zeitpunkt unseres Eintreffens bei didChange von dem, was erwartet wurde. 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];
    }]];
}
17
Owen Godfrey

Das Apple Docs To Many Relations besagt: Sie sollten auf den Proxy-Mutable-Set oder den Ordered-Set mit zugreifen

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

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

10
Nicolas Manzini

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

  • verwenden Sie die Kategorie Objective-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
9
Danik

Wenn Sie Mogenerator verwenden, dann statt

[parentObject add<Child>sObject:childObject];

benutze einfach:

[[parent object <child>sSet] addObject:childObject];
8
Καrτhικ

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

Dies beseitigt die Notwendigkeit für Kategorien, die später mit einer Lösung von Apple zum generierten Code in Konflikt geraten könnten, wenn der Fehler behoben ist.

Dies hat den zusätzlichen Vorteil, dass es der offizielle Weg ist, dies zu tun!

7
Grouchal

Es scheint, dass, wenn Sie das Elternteil mit dem Kind verbinden, indem Sie das Elternteil auf das Kind einstellen und nicht umgekehrt, es ohne Absturz funktioniert.

Also wenn du tust:

[child setParent:parent]

anstelle von

[parent setChildObects:child]

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

5
Cata

Dieses Problem trat bei der Migration eines Projekts von Objective-C nach Swift 2 mit XCode 7 auf. Das Projekt hat früher funktioniert, und das aus einem guten Grund: Ich habe MOGenerator verwendet, der Ersatzmethoden hatte, um diesen Fehler zu beheben. Aber nicht alle Methoden erfordern einen Ersatz.

Hier ist die Komplettlösung mit einer Beispielklasse, die sich so weit wie möglich auf Standard-Accessoren verlässt.

Nehmen wir an, wir haben eine Liste mit bestellten Artikeln

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

item.list = list

anstatt

list.addItemsObject(item)

Nun, wenn das keine Option ist, hier ist 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 auf die Idee gekommen bin :)

3
Nycen

Ich habe dieses Problem einfach übersehen und es mit einer viel einfacheren Implementierung gelöst als die anderen hier beschriebenen. Ich benutze einfach die Methoden von NSManagedObject, um mit Beziehungen umzugehen, wenn ich keine Unterklassen benutze.

Eine Beispielimplementierung zum Einfügen einer Entität in eine NSOrderedSet-Beziehung sieht folgendermaßen aus:

- (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 einwandfrei und wurde von mir verwendet, bevor ich zu NSManagedObject Unterklassen gewechselt bin.

3
Mic Pringle

Ich bin damit einverstanden, dass es hier vielleicht einen Fehler gibt. Ich habe die Implementierung des Befehls 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 die Neuzuweisung des Satzes zu "self.subitems" wird sichergestellt, dass die Benachrichtigungen "Will/DidChangeValue>" gesendet werden.

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

3
DisableR

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

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 besteht darin, dass der Code von Apple verwendet wird und dass er einfach und sauber ist. Außerdem wird das Set automatisch hinzugefügt und alle Beobachter werden aktualisiert. Kein Problem.

3
Owen Godfrey

Ich habe dieses Problem gelöst, indem ich die Inverse auf No Inverse gesetzt habe. Ich weiß nicht warum. Vielleicht gibt es Apple Bug . enter image description here

2
LevinYan

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

Die Antwort war CODE KEINE METHODE OR ZUSÄTZLICHE DINGE in deinen NSManagedObject-Unterklassen. Ich denke, es ist ein Kompilierungsfehler. Sehr seltsamer Fehler.

Ich hoffe es hilft ..

2
lobodart

Ich denke, jeder vermisst das eigentliche Problem. Dies liegt nicht an den Zugriffsmethoden, sondern an der Tatsache, dass NSOrderedSet keine Unterklasse von NSSet ist. Also, wenn -interSectsSet: wird mit einer geordneten Menge als Argument aufgerufen, was fehlschlägt.

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 die Korrektur darin besteht, die Implementierung der Set-Operatoren so zu ändern, dass sie die Typen transparent behandeln. Kein Grund, warum ein -intersectsSet: sollte entweder mit einer geordneten oder einer ungeordneten Menge funktionieren.

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

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
2
Entropy

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 removeSignals: -Methode. Dieser Override 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 -20 Elemente, sodass die Leistung kein großes Problem darstellt. Bitte weisen Sie jedoch auf relevante Punkte hin.

Vielen Dank,

Damien

1

Ich habe einen Fix für diesen Fehler gefunden, der für mich funktioniert. Ich ersetze dies nur:

[item addSubitemsObject:subItem];

mit diesem:

item.subitemsObject = subItem;
1
Bimawa

Ich habe diese Frage gefunden, indem ich nach der Fehlermeldung gegoogelt habe, und wollte nur darauf hinweisen, dass ich auf eine etwas andere Weise auf diesen Fehler gestoßen bin (ohne geordnete Mengen zu verwenden). Dies ist keine vollständige Antwort auf die gegebene Frage, aber ich veröffentliche sie hier, nur für den Fall, dass sie für andere hilfreich ist, die beim Suchen über diese Frage stolpern.

Ich habe eine neue Modellversion hinzugefügt und einige Beziehungen zu vorhandenen Modellen hinzugefügt und die add * Object-Methoden in der Headerdatei selbst definiert. Als ich versuchte, sie anzurufen, wurde der obige Fehler angezeigt.

Nachdem ich meine Modelle überprüft hatte, stellte ich fest, dass ich dummerweise vergessen hatte, das Kontrollkästchen "Zu viele Beziehungen" zu aktivieren.

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

1
BenV

Ich bin mir ziemlich sicher, dass es endlich soweit ist behoben in iOS 10 Beta 6 !

0
an0

Ich stellte fest, dass die Methode von LeeIII funktioniert, aber beim Erstellen von Profilen stellte ich fest, dass sie extrem langsam war. Das Parsen von 1000 Elementen dauerte 15 Sekunden. Aus dem Auskommentieren des Codes zum Hinzufügen der Beziehung wurden aus 15 Sekunden 2 Sekunden.

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

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

0
Robert

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
0
emreoktem

Robert,

Ich bin damit einverstanden, dass Ihre Antwort dafür funktioniert, aber denken Sie daran, dass es eine automatisch erstellte Methode gibt, mit der Sie einer Beziehung bereits eine ganze Reihe von Werten hinzufügen können. Die Dokumentation von Apple ( hier zu sehen im Abschnitt "Zu viele Beziehungen" oder hier im Abschnitt "Benutzerdefinierte Accessormethoden für Zu 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 ganz einfach Ihre Beziehungssätze außerhalb der Kerndaten zusammenstellen und dann alle auf einmal hinzufügen. Es könnte weniger hässlich sein als die von Ihnen vorgeschlagene Methode;)

0
JiuJitsuCoder