it-swarm.it

Eccezione generata negli accessori generati da NSOrderedSet

Sulla mia app Lion, ho questo modello di dati:

enter image description here

La relazione subitems dentro Itemè ordinata.

Xcode 4.1 (build 4B110) ha creato per me il file Item.h, Item.m, SubItem.h e SubItem.h.

Ecco il contenuto (generato automaticamente) di 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

Ed ecco il contenuto (generato automaticamente) di Item.m:

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

@implementation Item

@dynamic name;
@dynamic subitems;

@end

Come puoi vedere, la classe Item offre un metodo chiamato addSubitemsObject:. Sfortunatamente, quando provi ad usarlo in questo modo:

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

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

[item addSubitemsObject:subItem];

appare questo errore:

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

Mi potete aiutare?

Aggiornamento:

Dopo solo 1.787 giorni dalla mia segnalazione di bug, oggi (1 agosto 2016) Apple mi ha scritto questo: "Verifica questo problema con l'ultima versione di iOS 10 beta crea e aggiorna la tua segnalazione di bug su bugreport.Apple.com con i tuoi risultati. ". Speriamo che sia il momento giusto :)

363
Dev

Ho riprodotto la tua configurazione sia con il tuo modello di dati sia con una mia con nomi diversi. Ho avuto lo stesso errore in entrambi i casi.

Sembra un bug nel codice generato automaticamente di Apple.

263
TechZen

Sono d'accordo che potrebbe esserci un bug qui. Ho modificato l'implementazione del settatore di oggetti add per aggiungerlo correttamente a un NSMutableOrderedSet.

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

La riassegnazione del set a self.subitems garantirà l'invio delle notifiche Will/DidChangeValue.

244
InitJason

Ho deciso di migliorare la soluzione implementando tutti i metodi richiesti:

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

Sì, questo è sicuramente un bug di dati di base. Ho scritto una correzione basata su ObjC-Runtime qualche tempo fa, ma al momento ho pensato che sarebbe stato risolto presto. Comunque, nessuna fortuna, quindi l'ho pubblicato su GitHub come KCOrderedAccessorFix . Risolvi il problema su tutte le entità:

[managedObjectModel kc_generateOrderedSetAccessors];

Un'entità in particolare:

[managedObjectModel kc_generateOrderedSetAccessorsForEntity:entity];

O solo per una relazione:

[managedObjectModel kc_generateOrderedSetAccessorsForRelationship:relationship];
38
Sterling Archer

Invece di fare una copia suggerisco di usare l'accessor in NSObject per ottenere l'accesso a NSMutableOrderedSet delle relazioni.

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

per esempio. Core Data Release Notes for iOS v5. si riferiscono a questo.

In un breve test ha funzionato nella mia applicazione.

32
Stephan

Ho rintracciato il bug. Si verifica in willChangeValueForKey:withSetMutation:usingObjects:.

Questa chiamata fa scattare una catena di notifiche che possono essere difficili da tracciare, e ovviamente le modifiche a un risponditore possono avere implicazioni per un altro, che sospetto sia il motivo per cui Apple non ha fatto nulla.

Tuttavia, va bene in Set e sono solo le operazioni Set su un OrderedSet che funzionano male. Ciò significa che ci sono solo quattro metodi che devono essere modificati. Pertanto, tutto ciò che ho fatto è stato convertire le operazioni Set in operazioni Array equivalenti. Questi funzionano perfettamente e minimizzano le spese generali (ma necessarie).

A livello critico, questa soluzione presenta un difetto critico; se stai aggiungendo oggetti e uno degli oggetti esiste già, allora non viene aggiunto o spostato sul retro dell'elenco ordinato (non so quale). In entrambi i casi, l'indice ordinato previsto dell'oggetto al momento in cui arriviamo a didChange è diverso da quanto previsto. Questo potrebbe spezzare le app di alcune persone, ma non influisce sulle mie, poiché aggiungo sempre nuovi oggetti o confermo la loro posizione finale prima di aggiungerli.

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

Certo, c'è una soluzione più semplice. è il seguente;

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

Il Documenti Apple To Many Relations dice: dovresti accedere al set mutabile proxy o al set ordinato usando

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

La modifica di questo set aggiungerà o rimuoverà le relazioni all'oggetto gestito. Accedere al set ordinato mutabile utilizzando l'accessor sia con [] o. la notazione è sbagliata e fallirà.

10
Nicolas Manzini

Ricevuto lo stesso errore, la soluzione @LeeIII ha funzionato per me (grazie!). Suggerisco di modificarlo leggermente:

  • usa la categoria obiettivo-c per memorizzare il nuovo metodo (quindi non perderemo il nostro metodo se l'articolo viene generato di nuovo)
  • controlla se abbiamo già un set mutabile

Il contenuto di 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

Se stai usando mogenerator, allora invece di

[parentObject add<Child>sObject:childObject];

usa semplicemente:

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

Personalmente ho appena sostituito le chiamate ai metodi generati da CoreData con chiamate dirette al metodo come indicato in un'altra soluzione di @Stephan:

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

Ciò elimina la necessità di categorie che potrebbero successivamente entrare in conflitto con una soluzione da Apple al codice generato quando il bug è stato corretto.

Questo ha il vantaggio di essere il modo ufficiale per farlo!

7
Grouchal

Sembra che se colleghi il genitore al figlio impostando il genitore sul figlio e non viceversa, funziona senza crash.

Quindi se lo fai:

[child setParent:parent]

anziché

[parent setChildObects:child]

Dovrebbe funzionare, almeno funziona su iOS 7 e non ha avuto problemi con la relazione.

5
Cata

Questo problema si è verificato durante la migrazione di un progetto da Objective-C a Swift 2 con XCode 7. Quel progetto funzionava, e per una buona ragione: stavo usando MOGenerator che aveva metodi di sostituzione per correggere questo errore. Ma non tutti i metodi richiedono una sostituzione.

Ecco quindi la soluzione completa con una classe di esempio, facendo affidamento il più possibile sugli accessori predefiniti.

Diciamo che abbiamo un elenco con gli articoli ordinati

Prima una vincita veloce se hai una relazione uno/a-molti, il più semplice è semplicemente fare:

item.list = list

invece di

list.addItemsObject(item)

Ora, se non è un'opzione, ecco cosa puoi fare:

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

Certo, se stai usando Objective-C, puoi fare esattamente la stessa cosa poiché è qui che ho avuto l'idea in primo luogo :)

3
Nycen

Ho appena sbagliato questo problema e l'ho risolto utilizzando un'implementazione molto più semplice rispetto agli altri descritti qui. Uso semplicemente i metodi disponibili su NSManagedObject per gestire le relazioni quando non si usano le sottoclassi.

Un'implementazione di esempio per l'inserimento di un'entità in una relazione NSOrderedSet sarebbe simile alla seguente:

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

Funziona perfettamente ed è quello che stavo usando prima di passare alle sottoclassi NSManagedObject.

3
Mic Pringle

Sono d'accordo che forse c'è un bug qui. Ho modificato l'implementazione dell'oggetto add> setter per aggiungerlo correttamente a un NSMutableOrderedSet.

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

La riassegnazione del set a self.subitems garantirà l'invio delle notifiche Will/DidChangeValue>.

Leelll, sei sicuro che dopo tale installazione personalizzata dei valori NSMutableOrderedSet memorizzati in quel set verranno salvati correttamente nel database da CoreData? Non l'ho verificato, ma sembra che CoreData non sappia nulla di NSOrderedSet e si aspetta che NSSet sia un contenitore di relazioni per molti.

3
DisableR

Ho avuto lo stesso problema, ma solo quando ho provato qualcosa di diverso da quello che stavo facendo. Non riesco a vedere il codice per subItem, ma suppongo che abbia un collegamento inverso all'elemento. Chiamiamo questo reveres link "parentItem", quindi la soluzione più semplice è questa:

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'effetto è che utilizza il codice di Apple ed è semplice e pulito. Inoltre, il set viene aggiunto automaticamente a e tutti gli osservatori vengono aggiornati. Nessun problema.

3
Owen Godfrey

Ho risolto questo problema impostando l'inverso su No Inverse, non so perché, forse c'è Apple Bug . enter image description here

2
LevinYan

Ho appena riscontrato il problema in Swift (Xcode 6.1.1).

La risposta è stata NON CODIFICARE QUALUNQUE METODO OR COSE AGGIUNTIVE nelle sottoclassi di NSManagedObject. Penso che sia un errore del compilatore. Un bug molto strano ...

Spero che sia d'aiuto ..

2
lobodart

Penso che a tutti manchi il vero problema. Non è nei metodi di accesso ma piuttosto nel fatto che NSOrderedSet non è una sottoclasse di NSSet. Cosi quando -interSectsSet: viene chiamato con un set ordinato come argomento non riuscito.

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

 [setB intersectsSet:setA];

fallisce con *** -[NSSet intersectsSet:]: set argument is not an NSSet

Sembra che la correzione sia modificare l'implementazione degli operatori set in modo che gestiscano i tipi in modo trasparente. Nessun motivo per cui un -intersectsSet: dovrebbe funzionare con un set ordinato o non ordinato.

L'eccezione si verifica nella notifica di modifica. Presumibilmente nel codice che gestisce la relazione inversa. Dal momento che succede solo se ho impostato una relazione inversa.

Quanto segue ha fatto il trucco per me

@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

Ho la stessa situazione con un elemento chiamato "segnali" anziché "elementi secondari". La soluzione con tempset funziona nei miei test. Inoltre, ho riscontrato un problema con il metodo removeSignals:. Questa sostituzione sembra funzionare:

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

Se esiste un modo migliore per farlo, per favore fatemelo sapere. I miei valori immessi non superano mai i 10-20 articoli, quindi le prestazioni non destano grande preoccupazione, tuttavia si prega di indicare qualcosa di rilevante.

Grazie,

Damien

1

Ho trovato una correzione per questo bug che funziona per me. Sostituisco solo questo:

[item addSubitemsObject:subItem];

con questo:

item.subitemsObject = subItem;
1
Bimawa

Ho trovato questa domanda cercando su Google il messaggio di errore e volevo solo sottolineare che ho riscontrato questo errore in un modo leggermente diverso (non usando i set ordinati). Questa non è una risposta alla domanda data, ma la sto pubblicando qui nel caso in cui sia utile a chiunque si imbatta in questa domanda durante la ricerca.

Stavo aggiungendo una nuova versione del modello e ho aggiunto alcune relazioni ai modelli esistenti e ho definito personalmente i metodi add * Object nel file di intestazione. Quando ho provato a chiamarli, ho ricevuto l'errore sopra.

Dopo aver esaminato i miei modelli, mi sono reso conto che mi ero stupidamente dimenticato di selezionare la casella di controllo "To-Many Relationship".

Quindi, se ti stai imbattendo in questo e non stai usando set ordinati, ricontrolla il tuo modello.

1
BenV

Sono abbastanza sicuro che sia finalmente risolto in iOS 10 beta 6 !

0
an0

Ho scoperto che usare il metodo di LeeIII ha funzionato, ma sulla profilazione è risultato drasticamente lento. Ci sono voluti 15 secondi per analizzare 1000 oggetti. Commentando il codice per aggiungere la relazione trasformato 15 secondi in 2 secondi.

La mia soluzione alternativa (che è più veloce ma molto più brutta) prevede la creazione di un array temporaneo modificabile e la copia nel set ordinato quando tutto l'analisi è terminata. (questa è solo una vittoria della prestazione se hai intenzione di aggiungere molte relazioni).

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

Spero che questo aiuti qualcun altro!

0
Robert

Versione migliore della risposta corretta in Swift

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

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

Roberto,

Sono d'accordo che la tua risposta funzionerà per questo, ma tieni presente che esiste già un metodo creato automaticamente per aggiungere un intero set di valori a una relazione. La documentazione di Apple ( come mostrato qui nella sezione "Relazioni con molti" o qui nella sezione "Metodi di accesso personalizzati con le relazioni") li implementa in questo modo:

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

È possibile compilare facilmente il proprio insieme di relazioni al di fuori dei dati di base e quindi aggiungerle tutte in una volta utilizzando questo metodo. Potrebbe essere meno brutto del metodo che hai suggerito;)

0
JiuJitsuCoder