web-dev-qa-db-fra.com

Exception levée dans les accesseurs générés par NSOrderedSet

Sur mon application Lion, j'ai ce modèle de données:

enter image description here

La relation subitems à l'intérieur Itemest ordonnée.

Xcode 4.1 (build 4B110) a créé pour moi le fichier Item.h, Item.m, SubItem.h et SubItem.h.

Voici le contenu (généré automatiquement) de 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

Et voici le contenu (auto-généré) de Item.m:

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

@implementation Item

@dynamic name;
@dynamic subitems;

@end

Comme vous pouvez le constater, la classe Item propose une méthode appelée addSubitemsObject:. Malheureusement, lorsque vous essayez de l'utiliser de cette façon:

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

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

[item addSubitemsObject:subItem];

cette erreur apparaît:

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

Pouvez-vous m'aider?

Mise à jour:

Après seulement 1 787 jours de mon rapport de bogue, aujourd'hui (1er août 2016), Apple m'a écrit ceci: "Veuillez vérifier ce problème avec la dernière version iOS 10 bêta. votre rapport de bogue sur bugreport.Apple.com avec vos résultats. ". Espérons que c'est le bon moment :)

363
Dev

J'ai reproduit votre configuration à la fois avec votre modèle de données et l'une des miennes avec des noms différents. J'ai la même erreur dans les deux cas.

Cela ressemble à un bogue dans le code généré automatiquement par Apple.

263
TechZen

Je suis d'accord qu'il peut y avoir un bug ici. J'ai modifié l'implémentation du créateur d'objets ajouté pour ajouter correctement à un NSMutableOrderedSet.

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

Réaffecter le jeu à self.subitems garantira que les notifications Will/DidChangeValue sont envoyées.

244
InitJason

J'ai décidé d'améliorer la solution en implémentant toutes les méthodes requises:

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
Dmitry Makarenko

Oui, c’est définitivement un bogue Core Data. J'ai écrit un correctif basé sur ObjC-Runtime il y a quelque temps, mais à ce moment-là, je pensais qu'il serait bientôt corrigé. En tout cas, pas de chance, alors je l'ai posté sur GitHub comme KCOrderedAccessorFix . Contournez le problème sur toutes vos entités:

[managedObjectModel kc_generateOrderedSetAccessors];

Une entité en particulier:

[managedObjectModel kc_generateOrderedSetAccessorsForEntity:entity];

Ou juste pour une relation:

[managedObjectModel kc_generateOrderedSetAccessorsForRelationship:relationship];
38
Sterling Archer

Au lieu de faire une copie, je suggère d'utiliser l'accesseur dans NSObject pour accéder au NSMutableOrderedSet des relations.

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

par exemple. les Notes de version de Core Data pour iOS version 5. se réfèrent à cela.

Dans un court test cela a fonctionné dans mon application.

32
Stephan

J'ai suivi le bug. Cela se produit dans willChangeValueForKey:withSetMutation:usingObjects:.

Cet appel déclenche une chaîne de notifications qu'il peut être difficile de suivre, et bien sûr, les modifications apportées à un répondeur peuvent avoir des conséquences pour un autre, ce qui, je suppose, est la raison pour laquelle Apple n'a rien fait.

Toutefois, cela ne pose pas de problème avec Set et ses seules opérations sur Set sur un OrderedSet défectueux. Cela signifie qu'il n'y a que quatre méthodes qui doivent être modifiées. Par conséquent, tout ce que j'ai fait est de convertir les opérations Set en leurs opérations Array équivalentes. Ceux-ci fonctionnent parfaitement et les frais généraux minimes (mais nécessaires).

Sur un plan critique, cette solution souffre d’un seul défaut critique; si vous ajoutez des objets et que l'un des objets existe déjà, il n'est pas ajouté ou déplacé au dos de la liste ordonnée (je ne sais pas lequel). Dans les deux cas, l'index ordonné attendu de l'objet au moment où nous arrivons à didChange est différent de ce qui avait été prévu. Cela peut endommager les applications de certaines personnes, mais cela n’affecte pas la mienne, car j’ajoute toujours de nouveaux objets ou je confirme leur emplacement final avant de les ajouter.

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

Bien sûr, il existe une solution plus simple. c'est comme suit;

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

Le Apple docs à de nombreuses relations indique: vous devez accéder au jeu de proxy mutable ou au jeu ordonné à l'aide de

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

La modification de cet ensemble va ajouter ou supprimer des relations à votre objet géré. Accéder au jeu ordonné mutable à l’aide de l’accesseur, que ce soit avec [] ou. la notation est fausse et échouera.

10
Nicolas Manzini

A reçu la même erreur, la solution @LeeIII a fonctionné pour moi (merci!). Je suggère de le modifier légèrement:

  • utilisez la catégorie objective-c pour stocker la nouvelle méthode (nous ne perdrons donc pas notre méthode si Item est généré à nouveau)
  • vérifier si nous avons déjà ensemble mutable

Contenu de 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

Si vous utilisez mogenerator, alors au lieu de

[parentObject add<Child>sObject:childObject];

simplement utiliser:

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

Personnellement, je viens de remplacer les appels aux méthodes générées par CoreData par des appels directs à la méthode, comme indiqué dans une autre solution de @Stephan:

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

Cela évite d'avoir à recourir à des catégories susceptibles d'entrer en conflit avec une solution de Apple avec le code généré une fois le bogue corrigé.

Cela a l'avantage d'être la manière officielle de le faire!

7
Grouchal

Il semble que si vous associez le parent à l'enfant en définissant le parent sur l'enfant et non l'inverse, cela fonctionne sans panne.

Donc si vous faites:

[child setParent:parent]

au lieu de

[parent setChildObects:child]

Cela devrait fonctionner, au moins cela fonctionne sur iOS 7 et n'a pas eu de problèmes avec la relation.

5
Cata

Ce problème m'est survenu lors de la migration d'un projet d'Objective-C vers Swift 2 avec XCode 7. Ce projet fonctionnait jadis et pour une bonne raison: j’utilisais MOGenerator qui disposait de méthodes de remplacement pour résoudre ce problème. Mais toutes les méthodes ne nécessitent pas un remplacement.

Voici donc la solution complète avec un exemple de classe, s’appuyant autant que possible sur des accesseurs par défaut.

Disons que nous avons une liste avec les articles commandés

D'abord un gain rapide si vous avez une relation un à plusieurs, le plus simple est de le faire:

item.list = list

au lieu de

list.addItemsObject(item)

Maintenant, si ce n'est pas une option, voici ce que vous pouvez faire:

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

Bien sûr, si vous utilisez Objective-C, vous pouvez faire exactement la même chose puisque c'est là que j'ai eu l'idée à la base :)

3
Nycen

Je viens de craquer ce problème et de le résoudre en utilisant une implémentation beaucoup plus simple que les autres décrites ici. J'utilise simplement les méthodes disponibles sur NSManagedObject pour traiter les relations lorsque les sous-classes ne sont pas utilisées.

Un exemple d'implémentation pour l'insertion d'une entité dans une relation NSOrderedSet ressemblerait à ceci:

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

Cela fonctionne parfaitement et c’est ce que j’utilisais avant de passer aux sous-classes NSManagedObject.

3
Mic Pringle

Je suis d'accord qu'il y a peut-être un bug ici. J'ai modifié l'implémentation du paramètre add object> pour ajouter correctement à un NSMutableOrderedSet.

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

Réaffecter le jeu à self.subitems garantira que les notifications Will/DidChangeValue> sont envoyées.

Leelll, êtes-vous sûr qu'après cette configuration personnalisée de NSMutableOrderedSet, les valeurs stockées dans cet ensemble seront correctement enregistrées dans la base de données par CoreData? Je n'ai pas vérifié cela, mais il semblerait que CoreData ne connaisse rien de NSOrderedSet et attend NSSet comme conteneur de relations multiples.

3
DisableR

J'ai eu le même problème, mais seulement quand j'ai essayé quelque chose de différent de ce que je faisais. Je ne vois pas le code du sous-élément, mais je supposerai qu'il existe un lien inverse pour l'élément. Appelons ce lien respectueux, "parentItem", alors la solution la plus simple est la suivante:

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;

L'effet est qu'il utilise le propre code d'Apple et qu'il est simple et propre. De plus, l'ensemble est automatiquement ajouté à et tous les observateurs sont mis à jour. Aucun problème.

3
Owen Godfrey

J'ai résolu ce problème en définissant l'inverse de No Inverse, je ne sais pas pourquoi, peut-être qu'il y a Apple Bug . enter image description here

2
LevinYan

Je viens d'avoir le problème dans Swift (Xcode 6.1.1).

La réponse était NE CODEZ AUCUNE MÉTHODE OR CHOSES ADDITIONNELLES dans vos sous-classes NSManagedObject. Je pense que c'est une erreur de compilation. Bug très étrange ..

J'espère que ça aide ..

2
lobodart

Je pense que tout le monde passe à côté du vrai problème. Ce n'est pas dans les méthodes d'accès, mais plutôt dans le fait que NSOrderedSet n'est pas une sous-classe de NSSet. Ainsi, lorsque -interSectsSet: est appelé avec un jeu ordonné en argument, il échoue.

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

 [setB intersectsSet:setA];

échoue avec *** -[NSSet intersectsSet:]: set argument is not an NSSet

Il semble que le correctif consiste à modifier l'implémentation des opérateurs de l'ensemble afin qu'ils gèrent les types de manière transparente. Aucune raison pour laquelle un -intersectsSet: devrait fonctionner avec un ensemble ordonné ou non.

L'exception se produit dans la notification de modification. Probablement dans le code qui gère la relation inverse. Comme cela ne se produit que si je mets une relation inverse.

Ce qui suit a fait le tour pour moi

@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

J'ai la même situation avec un élément appelé "signaux" au lieu de "sous-éléments". La solution avec tempset fonctionne dans mes tests. De plus, j'ai eu un problème avec la méthode removeSignals:. Cette dérogation semble fonctionner:

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

S'il y a un meilleur moyen de le faire, s'il vous plaît faites le moi savoir. Les valeurs que je saisis ne sont jamais plus de 10 à 20 éléments; les performances ne sont donc pas un problème. Néanmoins, veuillez signaler tout élément pertinent.

Merci,

Damien

1
Damien Del Russo

J'ai trouvé un correctif pour ce bogue qui fonctionne pour moi. Je viens de remplacer ceci:

[item addSubitemsObject:subItem];

avec ça:

item.subitemsObject = subItem;
1
Bimawa

J'ai trouvé cette question en recherchant le message d'erreur sur Google, et je voulais simplement signaler que j'ai rencontré cette erreur d'une manière légèrement différente (sans utiliser d'ensembles ordonnés). Ce n’est pas tout à fait une réponse à la question posée, mais je la poste ici au cas où cela serait utile à toute autre personne qui tombe par hasard sur cette question pendant la recherche.

J'étais en train d'ajouter une nouvelle version de modèle, d'ajouter des relations aux modèles existants et de définir moi-même les méthodes add * Object dans le fichier d'en-tête. Quand j'ai essayé de les appeler, j'ai eu l'erreur ci-dessus.

Après avoir examiné mes modèles, j'ai réalisé que j'avais bêtement oublié de cocher la case "Relation multiple".

Donc, si vous rencontrez ce problème et que vous n'utilisez pas d'ensembles ordonnés, vérifiez votre modèle.

1
BenV

Je suis sûr que c'est finalement corrigé dans iOS 10 beta 6 !

0
an0

J'ai trouvé que l'utilisation de la méthode de LeeIII fonctionnait, mais que le profilage a révélé qu'elle était extrêmement lente. Il a fallu 15 secondes pour analyser 1000 éléments. En commentant le code pour ajouter la relation transformée en 15 secondes en 2 secondes.

Ma solution de contournement (qui est plus rapide mais beaucoup plus laide) consiste à créer un tableau temporaire mutable puis à copier dans l'ensemble ordonné lorsque tout l'analyse est terminée. (Ce n'est qu'une performance gagnante si vous allez ajouter de nombreuses relations).

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

J'espère que cela aidera quelqu'un d'autre!

0
Robert

Meilleure version de la bonne réponse dans Swift

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

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

Robert,

Je conviens que votre réponse fonctionnera pour cela, mais gardez à l'esprit qu'il existe déjà une méthode créée automatiquement pour ajouter tout un ensemble de valeurs à une relation. La documentation d'Apple ( comme on le voit ici sous la section "Relations multiples" ou ici sous la section "Méthodes personnalisées d'accesseur de relations multiples") est implémentée de la manière suivante:

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

Vous pouvez facilement compiler votre ensemble de relations en dehors des données de base, puis les ajouter en une fois à l'aide de cette méthode. Cela pourrait être moins moche que la méthode que vous avez suggérée;)

0
JiuJitsuCoder