Zgłoszono wyjątek w akcesoriach generowanych przez NSOrdersSet

364

W mojej aplikacji Lion mam ten model danych:

wprowadź opis zdjęcia tutaj

Relacja subitemswewnątrz Item jest uporządkowana .

Xcode 4.1 (build 4B110) stworzył dla mnie plik Item.h, Item.m, SubItem.hi SubItem.h.

Oto treść (automatycznie generowana) 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

A oto treść (automatycznie generowana) Item.m:

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

@implementation Item

@dynamic name;
@dynamic subitems;

@end

Jak widać, klasa Itemoferuje metodę o nazwie addSubitemsObject:. Niestety, próbując użyć go w ten sposób:

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

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

[item addSubitemsObject:subItem];

pojawia się ten błąd:

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

Możesz mi pomóc?

Aktualizacja:

Po zaledwie 1787 dniach od zgłoszenia błędu dzisiaj (1 sierpnia 2016 r.) Apple napisał do mnie: „Sprawdź ten problem za pomocą najnowszej wersji beta systemu iOS 10 i zaktualizuj swój raport o błędach na stronie bugreport.apple.com z wynikami”. . Miejmy nadzieję, że to właściwy czas :)

Dev
źródło
5
Widzę ten sam problem. Mam nadzieję, że wkrótce zostanie naprawiony. Chociaż bezpośrednie użycie zmiennego zestawu uporządkowanego jest na razie łatwym obejściem. Uwaga: używam mogeneratora, ale zakładam, że wewnętrznie używa tego samego generatora Apple dla tej części generowanego kodu.
Chad Podoski
12
To prawie 2 lata! Naprawisz to w iOS 7, Apple? —— Chcę tylko podzielić się z tymi, którzy zastanawiają się, czy ten błąd nadal występuje: „Tak, to prawda”.
an0
1
Już prawie dwa lata temu jest to problem we wszystkich wersjach podglądu programisty xcode 5.
Korvin Szanto
2
Czy nadal widzisz problem, jeśli używasz odpowiedniego akcesorium KVC? (tj. mutableOrderedSetValueForKey:)
quell
3
Wygląda na to, że wciąż stanowi problem w Mavericks.
Tim

Odpowiedzi:

263

Odtworzyłem twoją konfigurację zarówno z twoim modelem danych, jak i jednym z moich własnych o różnych nazwach. W obu przypadkach dostałem ten sam błąd.

Wygląda na błąd w automatycznie generowanym kodzie Apple.

TechZen
źródło
60
Identyfikator błędu to 10114310. Zgłoszono go 13 września 2011 r., Ale dziś (15 stycznia 2012 r.) Nadal jest „otwarty”. To niesamowite, biorąc pod uwagę liczbę osób, które mają ten sam problem.
Dev
14
Aktualizacja: dzisiaj (11 maja 2012 r.) Błąd nr 10114310 jest nadal otwarty, 241 dni po moim raporcie (13 września 2011 r.). Nie do wiary.
Dev
23
Właśnie omawiałem to z inżynierem Apple podczas jednej z sesji CoreData Lab w WWDC. Zdają sobie sprawę z tego, że jest to prawdziwy błąd, i z tego, co widziałem, ma status „krytyczny”, ale oczywiście nie ma obietnicy, kiedy to naprawi. Nie sądzę, że zostanie to naprawione w iOS6 / Mountain Lion. Myślę, że lepiej byłoby zduplikować ten radar. Obecnie ma około 25 dup, tym więcej, tym lepiej!
DaGaMs
40
Właśnie sprawdzony dzisiaj, nadal jest dostępny w iOS 7 GM / OMG! Nie mogę w to uwierzyć…
an0
79
Aktualizacja: 797 dni, minęły 2 nowe główne wersje iOS i niezliczone wersje Xcode, odkąd wypełniłem błąd # 10114310. I wciąż jest „otwarty”. Nie do wiary.
Dev
244

Zgadzam się, że może tu być błąd. Zmodyfikowałem implementację narzędzia do ustawiania obiektów dodawania, aby poprawnie dołączała się do NSMutableOrdersSet.

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

Ponowne przypisanie zestawu do self.subitems zapewni wysłanie powiadomień Will / DidChangeValue.

InitJason
źródło
Twój fragment kodu był dokładnie tym, czego potrzebowałem, aby obejść ten problem. Mam nadzieję, że Apple ostatecznie rozwiązuje ten problem, ale do tej pory nie widziałem żadnych problemów z używaniem twojego podejścia.
Christopher Hujanen,
Otrzymuję ten błąd, gdy próbuję zaimplementować to obejście. [__NSArrayI isEqualToSet:]: nierozpoznany selektor wysłany do instancji ... Zwykle pochodzi z elementu, który został wydany, ale nie może znaleźć gdzie, ktoś biegnie zaangażowany w to?
DerekH,
@DerekH isEqualToSet jest metodą, którą posiada tylko NSSet, więc zgaduję, że przekonwertowałeś, utworzyłeś lub traktujesz wskaźnik jako NSArray przed przekazaniem z powrotem do NSManagedObject, który powinien, jeśli jakiś powód wywołać isEqualToOragedSet, aby ustalić, czy zestaw potrzebuje nawet zmienić lub zostać takim, jakim jest.
InitJason
3
@MarkAmery Testowany. Zweryfikowano Zestaw dynamiczny seter self.subitems wysyła powiadomienia. Więc rozwiązanie JLust jest poprawne.
Bernstein
3
To dobra odpowiedź, ale jest nieefektywna. Skopiujesz cały zamówiony zestaw, zmodyfikujesz, a następnie skopiujesz go z powrotem. Efekt jest nie tylko trafieniem do zamówionego zestawu, ale wysyłane są powiadomienia, że ​​za każdym razem, gdy zmieniany jest zamówiony zestaw, zmienia się cała jego zawartość! Jeśli ten uporządkowany zestaw jest używany na przykład dla UITable, może to mieć poważne konsekwencje dla aktualizacji. W swoim rozwiązaniu przedstawiłem dokładnie, skąd pochodzi błąd i pokazałem bardziej wydajną metodę obejścia błędu.
Owen Godfrey
111

Zdecydowałem się ulepszyć rozwiązanie, wdrażając wszystkie wymagane metody:

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];
    }
}
Dmitrij Makarenko
źródło
1
Jaki jest typ awarii? Funkcja „removeObjectFromSubitemsAtIndex” nie usuwa tych podelementów, nadal istnieją one w Twojej pamięci, to tylko sposób na usunięcie relacji między obiektami.
Dmitry Makarenko,
2
kItemsKey to stała, która została dodana tylko dla wygody w wywołaniach metod KVO. To nazwa uporządkowanego związku, dla którego piszesz swoje metody.
Dmitry Makarenko
1
Tak mi się wydaje. Dzięki. Ale moim problemem jest to, że dane nie są zapisywane w bazie danych przy użyciu tych metod.
Bagusflyer
4
!!!!!!!!! Wystarczy skopiować kod i zmienić nazwy metod, działa idealnie !!! To najszybsza odpowiedź.
flypig
1
To wspaniałe, ale tworzenie tymczasowej kopii zamówionego zestawu nie jest konieczne. Sprawcą jest to willChangeValueForKey:withSetMutation:usingObjects, czego udało ci się uniknąć. Następnie użyj [[self primitiveValueForKey:ChildrenKey] unionOrderedSet:values]lub [[self primitiveValueForKey:ChildrenKey] minusOrderedSet:values]odpowiednio. Zobacz moją odpowiedź, aby poznać szczegóły.
Owen Godfrey
38

Tak, to zdecydowanie błąd Core Data. Jakiś czas temu napisałem poprawkę opartą na środowisku wykonawczym ObjC, ale w tym czasie pomyślałem, że wkrótce zostanie naprawiona. W każdym razie, nie ma takiego szczęścia, więc opublikowałem to na GitHub jako KCOragedAccessorFix . Obejmij problem we wszystkich swoich podmiotach:

[managedObjectModel kc_generateOrderedSetAccessors];

W szczególności jeden podmiot:

[managedObjectModel kc_generateOrderedSetAccessorsForEntity:entity];

Lub tylko dla jednego związku:

[managedObjectModel kc_generateOrderedSetAccessorsForRelationship:relationship];
Sterling Archer
źródło
Zastanawiam się, czy będzie to kolidować z prawdziwą poprawką firmy Apple, czy nie?
tia
3
Nie powinno to kolidować z poprawką Apple, ponieważ jej celem jest zastąpienie implementacji Apple bez względu na wszystko. Kiedy / jeśli jest to naprawione przez Apple, może dodam - (BOOL)kc_needsOrderedSetAccessorFix;coś, co sprawdza wersję Foundation / iOS.
Sterling Archer
2
W repozytorium głównym CocoaPods znajduje się plik KCOrdersAccessorFix.podspec. Aby więc połączyć to z twoimi projektami, możesz po prostu dodać „pod” KCOrdersAccessorFix ”do swojego Podfile
Anton Matosov
Miało to pewne problemy z iOS 8 (nieprawidłowe podpisy metod objc_msg_send)
NSTJ
Na iOS9 to działa, niezła robota! To najlepsze rozwiązanie w historii, nie trzeba niczego zmieniać w kodzie!
Borzh
32

Zamiast zrobić kopię, sugeruję użyć akcesorium w NSObject, aby uzyskać dostęp do NSMutableOrdersSet relacji.

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

np. Informacje o wydaniu Core Data dla iOS 5.0 odnoszą się do tego.

W krótkim teście zadziałało w mojej aplikacji.

Stephan
źródło
1
Nie można tak łatwo refaktoryzować literałów. Jeśli używasz kodu, kompilator może wpisać check self.subitems.
logancautrell
1
@logancautrell tak to jest poprawne. Zależy to od priorytetu konkretnego przypadku użycia. Ogólnie skupiam się na oszczędzaniu zasobów, szczególnie w tym przypadku, ponieważ było to tylko obejście.
Stephan
2
Dosłowny ciąg znaków można jednak zastąpić NSStringFromSelector(@selector(subitems)):)
Ja͢ck
17

Śledziłem błąd. Występuje w willChangeValueForKey:withSetMutation:usingObjects:.

To wywołanie uruchamia łańcuch powiadomień, które mogą być trudne do śledzenia, i oczywiście zmiany w jednym z respondentów mogą mieć wpływ na inny, co podejrzewam, że Apple nic nie zrobił.

Jednak w Set jest w porządku, a to tylko operacje Set na zestawie OrdersSet, które działają nieprawidłowo. Oznacza to, że są tylko cztery metody, które należy zmienić. Dlatego wszystko, co zrobiłem, to przekonwertowanie operacji Set na ich równoważne operacje Array. Działają one idealnie i minimalne (ale konieczne) koszty ogólne.

Na poziomie krytycznym to rozwiązanie ma jedną wadę krytyczną; jeśli dodajesz obiekty, a jeden z nich już istnieje, to nie jest on dodawany ani przenoszony na koniec listy uporządkowanej (nie wiem, która). W obu przypadkach oczekiwany uporządkowany indeks obiektu w chwili dotarcia do nas didChangeróżni się od oczekiwanego. Może to zepsuć aplikacje niektórych osób, ale nie wpływa to na moje, ponieważ zawsze dodam nowe obiekty lub potwierdzam ich ostateczne położenie przed ich dodaniem.

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

Oczywiście istnieje łatwiejsze rozwiązanie. wygląda to następująco;

- (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
źródło
Szkoda, że ​​wszyscy inni przeoczyli tę odpowiedź, zdecydowanie wydaje się najlepszym rozwiązaniem.
George
To rozwiązanie ma znacznie lepszą wydajność niż te, które używają uporządkowanegoSetWithOrdersSet do utworzenia zestawu lokalnego. Ma to duże obciążenie, gdy masz duże zestawy danych. Wydaje się, że łatwiejszym rozwiązaniem jest po prostu refaktoryzowana wersja początkowa z nie pokazanymi metodami.
David Pettigrew
1
Nadal widzę awarię w addChildren: *** Zamykanie aplikacji z powodu nieprzechwyconego wyjątku „NSInvalidArgumentException”, powód: „- [TrackHistory insertTrackpoints: atIndexes:]: nierozpoznany selektor wysłany do instancji 0x1702b1b20”
Victor Bogdan
@OwenGodfrey Dla łatwiejszego rozwiązania, gdzie wdrażasz te metody? Otrzymuję wyjątek: [Parent insertObject: inChildrenAtIndex:] nierozpoznany selektor wysłany do instancji 0x6180000ac480.
Dalmazio,
twoja zmienna to „Parent” z dużym „P”? Czy to oznacza, że ​​nazywasz klasę „Parent”, czy masz zmienną instancji o nazwie „Parent”? Jeśli moją klasą jest Parent, to zaimplementowałem te metody na dole Parent, ale trzeba by było wywołać ją w instancji, która najprawdopodobniej byłaby nazwana „rodzicem” z małą literą „p”, ponieważ nie są to metody klasowe .
Owen Godfrey,
10

Dokument Apple To Many Relations mówi: powinieneś uzyskać dostęp do zestawu mutable proxy lub zestawu uporządkowanego za pomocą

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

Modyfikacja tego zestawu spowoduje dodanie lub usunięcie relacji do zarządzanego obiektu. Dostęp do mutable uporządkowanego zestawu za pomocą akcesorium czy za pomocą [] lub. notacja jest błędna i zawiedzie.

Nicolas Manzini
źródło
3
Aby być uczciwym, doktorzy mówią również: „lub jedną z automatycznie generowanych metod mutatora relacji (patrz Metody generowania dynamicznie):”
Mat.
Okej, ok ... masz rację. Cóż, powiedzmy, że jest to najprostszy działający sposób ...
Nicolas Manzini,
9

Otrzymałem ten sam błąd, rozwiązanie @LeeIII zadziałało dla mnie (dzięki!). Sugeruję nieznaczną modyfikację:

  • użyj kategorii C celu do zapisania nowej metody (więc nie stracimy naszej metody, jeśli przedmiot zostanie ponownie wygenerowany)
  • sprawdź, czy mamy już zestaw zmiennych

Treść 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
źródło
Warto przenieść ten kod do kategorii. Ale nadal musimy uwzględnić faktyczne dodawanie / usuwanie za pomocą wywołań will / setPrimitiveValue / didChange, jak w odpowiedzi @Dmitry Makarenko.
Vladimir Shutyuk
8

Jeśli używasz mogeneratora, to zamiast

[parentObject add<Child>sObject:childObject];

po prostu użyj:

[[parent object <child>sSet] addObject:childObject];
Καrτhικ
źródło
Ponieważ mogenerator dba o dodatkowy kod, który w innym przypadku musiałby zostać napisany i po prostu umożliwia dostęp do bazowego obiektu zestawu.
Καrτhικ
Wygląda na to poprawka została właśnie popełnił że środki mogenerator wygeneruje skorygowane ciał ... github.com/dmakarenko/mogenerator/commit/...
kombinatorycznej
1
Używam, mogeneratorale nadal mam błąd.
Colas,
7

Osobiście właśnie zastąpiłem wywołania metod wygenerowanych przez CoreData bezpośrednimi wywołaniami metody opisanymi w innym rozwiązaniu przez @Stephan:

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

Eliminuje to potrzebę kategorii, które mogą później kolidować z rozwiązaniem Apple wygenerowanego kodu, gdy błąd zostanie naprawiony.

Ma to tę dodatkową zaletę, że jest to oficjalny sposób!

Grouchal
źródło
Daje to następujący błąd: „[<<KLASA 0x20886d10> wartośćForUndefinedKey:]: ta klasa nie jest zgodna z kodowaniem wartości klucza dla podelementów klucza.”
jmstone617,
Chociaż wciąż nie denerwuje mnie to, że nie jest to wymienione w znanych problemach Apple'a (otworzyłem radar dla pozornie daremnego gestu, jaki to jest), to rozwiązanie zadziałało dla mnie bezbłędnie.
Scott Corscadden,
Chciałbym zobaczyć tę odpowiedź wcześniej;
Używałem
Dlaczego addObject:dzwonimy dwa razy?
Jason Moore,
5

Wydaje się, że jeśli połączysz rodzica z dzieckiem, ustawiając go, a nie na odwrót, działa to bez awarii.

Więc jeśli to zrobisz:

[child setParent:parent]

zamiast

[parent setChildObects:child]

Powinien działać, przynajmniej działa na iOS 7 i nie miał żadnych problemów z relacją.

Cata
źródło
1
Nie robi wiele dobrego, gdy obie strony są zbyt wielu. Wtedy nie ma wyraźnego związku rodzic-dziecko.
fatuhoku
3

Miałem ten sam problem, ale tylko wtedy, gdy próbowałem czegoś innego niż to, co robiłem. Nie widzę kodu dla podelementu, ale założę, że ma on link zwrotny do elementu. Nazwijmy to odsłonięciem linku „parentItem”, wtedy najłatwiejszym rozwiązaniem jest:

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;

Skutkuje to tym, że wykorzystuje własny kod Apple i jest prosty i czysty. Ponadto zestaw jest dodawany automatycznie, a wszyscy obserwatorzy są aktualizowani. Nie ma problemu.

Owen Godfrey
źródło
To jest bardzo miłe. Rozwiązuje cały problem i utrzymuje go w porządku. Nadal szalone, że błąd jest nadal obecny. Inną zaletą tej odpowiedzi jest to, że jeśli zregenerujesz swoje podstawowe modele danych, nie musisz przepisywać poprawek. Dzięki!
Johan S
Zobacz moją drugą odpowiedź. Śledziłem błąd bardziej szczegółowo. To wciąż najłatwiejszy sposób, ale druga metoda jest najlepsza, ponieważ otwiera więcej możliwości.
Owen Godfrey,
Łał! Wreszcie!!! Dzięki! (Próbowałem swój inny kod, ale dostał błędy, coś o tym niewłaściwy typ został wysłany [self didChange: NSKeyValueChangeInsertion valuesAtIndexes: indexSet forKey: ChildrenKey];)
Leonard Pauli
3

Właśnie zepsułem ten problem i rozwiązałem go, stosując znacznie prostszą implementację niż inne opisane tutaj. Po prostu korzystam z metod dostępnych naNSManagedObject radzenia sobie z relacjami, gdy nie używam podklas.

Przykładowa implementacja wstawiania encji do NSOrderedSetrelacji wyglądałaby następująco:

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

Działa to doskonale i tego właśnie używałem przed przejściem do NSManagedObjectpodklas.

Mic Pringle
źródło
3

Ten problem pojawił się podczas migracji projektu z Objective-C do Swift 2 z XCode 7 . Ten projekt kiedyś działał i nie bez powodu: korzystałem z MOGeneratora, który miał metody zastępcze, aby naprawić ten błąd. Ale nie wszystkie metody wymagają wymiany.

Oto kompletne rozwiązanie z przykładową klasą, w miarę możliwości oparte na domyślnych akcesoriach.

Załóżmy, że mamy listę z zamówionymi przedmiotami

Najpierw szybka wygrana, jeśli masz relację jeden do wielu, najłatwiej jest po prostu zrobić:

item.list = list

zamiast

list.addItemsObject(item)

Teraz, jeśli nie jest to opcja , oto co można zrobić:

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

Oczywiście, jeśli używasz Objective-C, możesz zrobić dokładnie to samo, ponieważ to tutaj wpadłem na pomysł :)

Nycen
źródło
3

Zgadzam się, że może tutaj jest błąd. Zmodyfikowałem implementację dodawania obiektu> settera, aby poprawnie dołączała się do NSMutableOrdersSet.

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

Ponowne przypisanie zestawu do self.subitems zapewni wysłanie powiadomień Will / DidChangeValue>.

Leelll, czy jesteś pewien, że po takiej niestandardowej konfiguracji wartości NSMutableOrdersSet zapisanych w tym zestawie zostaną poprawnie zapisane w bazie danych przez CoreData? Nie sprawdziłem tego, ale wygląda na to, że CoreData nic nie wie o NSOrdersSet i oczekuje, że NSSet będzie kontenerem wielu relacji.

Wyłącz
źródło
Aby CoreData zwrócił lub wziął obiekt NSOrdersSet, musi być spełnionych wiele warunków, jak to pytanie rozpoczęło. Najczęstsze błędy, które widzę, gdy osoby udostępniające mój kod są programistami, którzy nie używają Lion. Środowisko NSOrdersSets nie jest dostępne w snowleopard. Ale tak, nie widziałem tego niepowodzenia, chociaż nie jestem pewien, czy jest to najlepsze pod względem wydajności. Domyślam się, że zajmuje to cały zestaw i zastępuje go zamiast po prostu wstawić żądany rekord.
InitJason
2

Myślę, że wszystkim brakuje prawdziwego problemu. Nie dotyczy to metod akcesora, ale raczej faktu, że NSOrderedSetnie jest to podklasa NSSet. Więc gdy -interSectsSet:zostanie wywołany z uporządkowanym zestawem jako argumentem, zawiedzie.

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

 [setB intersectsSet:setA];

nie działa z *** -[NSSet intersectsSet:]: set argument is not an NSSet

Wygląda na to, że poprawka polega na zmianie implementacji operatorów zbioru, aby obsługiwali typy w sposób przejrzysty. Bez powodu, dla którego:-intersectsSet: powinien pracować z zestawem uporządkowanym lub nieuporządkowanym.

Wyjątek występuje w powiadomieniu o zmianie. Przypuszczalnie w kodzie, który obsługuje odwrotną relację. Ponieważ dzieje się tak tylko wtedy, gdy ustawię odwrotną relację.

Poniższe zrobiły dla mnie lewę

@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
Entropia
źródło
2

Właśnie dostałem problem w Swift (Xcode 6.1.1).

Odpowiedź brzmiała: NIE KODUJ ŻADNEJ METODY ANI DODATKOWYCH RZECZY w podklasach NSManagedObject. Myślę, że to błąd kompilatora. Bardzo dziwny błąd ..

Mam nadzieję, że to pomoże ..

lobodart
źródło
3
Jeśli więc nie mogę zaimplementować innych poprawek, co powinienem zrobić, aby to naprawić?
Ben Leggiero,
2

Rozwiązałem ten problem, ustawiając odwrotność na No Inverse, nie wiem dlaczego, może jest błąd Apple.wprowadź opis zdjęcia tutaj

LevinYan
źródło
1

Mam taką samą sytuację z elementem o nazwie „sygnały” zamiast „podelementy”. Rozwiązanie z tempsetem działa w moich testach. Ponadto miałem problem z metodą removeSignals:. To zastąpienie wydaje się działać:

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

Jeśli istnieje lepszy sposób, prosimy o kontakt. Moje wartości wejściowe nigdy nie są większe niż 10-20 pozycji, więc wydajność nie stanowi większego problemu - niemniej prosimy o wskazanie czegoś istotnego.

Dzięki,

Damien

Damien Del Russo
źródło
1

Znalazłem to pytanie, przeglądając komunikat o błędzie, i chciałem tylko zauważyć, że napotkałem ten błąd w nieco inny sposób (nie używając uporządkowanych zestawów). To nie jest całkiem odpowiedź na dane pytanie, ale zamieszczam je tutaj, na wypadek, gdyby było pomocne dla każdego, kto natknie się na to pytanie podczas wyszukiwania.

Dodałem nową wersję modelu, dodałem pewne relacje do istniejących modeli i sam zdefiniowałem metody add * Object w pliku nagłówkowym. Kiedy próbowałem do nich zadzwonić, otrzymałem błąd powyżej.

Po przejrzeniu moich modeli zdałem sobie sprawę, że głupio zapomniałem zaznaczyć pole wyboru „Relacja z wieloma osobami”.

Jeśli więc napotykasz na to i nie używasz uporządkowanych zestawów, sprawdź dokładnie swój model.

BenV
źródło
1

Znalazłem naprawę tego błędu, który działa dla mnie. Właśnie zastępuję to:

[item addSubitemsObject:subItem];

z tym:

item.subitemsObject = subItem;
Bimawa
źródło
1

Lepsza wersja poprawnej odpowiedzi w SWIFT

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

tempSet.add(newItem)
parent!.subItems = tempSet
emreoktem
źródło
0

Odkryłem, że metoda LeeIII zadziałała, ale przy profilowaniu okazało się, że była drastycznie powolna. Parsowanie 1000 elementów zajęło 15 sekund. Komentowanie kodu w celu dodania relacji zamieniło 15 sekund w 2 sekundy.

Moje obejście (który jest szybszy, ale o wiele bardziej brzydki) wymaga utworzenia tymczasowego zmienny tablicę następnie kopiując do uporządkowanego zbioru, gdy wszystko parsowanie jest wykonywana. (jest to tylko wygrana wydajności, jeśli zamierzasz dodać wiele relacji).

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

Mam nadzieję, że pomoże to komuś innemu!

Robert
źródło
0

Robert,

Zgadzam się, że twoja odpowiedź na to zadziała, ale pamiętaj, że istnieje już automatycznie utworzona metoda dodawania całego zestawu wartości do relacji. Dokumentacja Apple'a ( jak widać tutaj w sekcji „Relacje wielu osób” lub tutaj w sekcji „Niestandardowe metody akcesora relacji”) implementuje je w ten sposób:

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

Za pomocą tej metody można łatwo skompilować zestaw relacji poza podstawowymi danymi, a następnie dodać je wszystkie naraz. Może być mniej brzydka niż sugerowana metoda;)

JiuJitsuCoder
źródło