7

To długotrwały problem podczas używania danych podstawowych dla wielu relacji, że bardzo trudno jest posortować żądanie pobierania przy użyciu NSSortDescriptor w jednostce Parent na podstawie liczba children występuje w relacji jeden-do-wielu z jednostką Child. Jest to szczególnie przydatne w połączeniu z NSFetchedResultsController. Zazwyczaj inicjowanie deskryptor sortowania jako:NSSortDescriptor do sortowania według liczby pozycji w danych podstawowych Wiele zależności

NSSortDescriptor *sortByNumberOfChildren = [[NSSortDescriptor alloc] initWithKey:@"[email protected]" ascending:NO]; 

powoduje wyjątek 'Keypath containing KVC aggregate where there shouldn't be one; failed to handle [email protected]

Na iOS 6.1, odkryłem poprawkę poprzez dodanie akcesor KVO -countOf<Key> jako atrybut do mojego zarządzanego modelu obiektowego jako liczba całkowita rodzaj. NIE zaimplementowałem niczego dla tego atrybutu w mojej podklasie NSManagedObject, ponieważ cała magia zdaje się dziać pod maską. (patrz https://stackoverflow.com/a/15546371/2042527).

Nie działa to jednak pod adresem iOS 6.0. Tu okazało się, że dodanie następującej metody do NSManagedObject podklasy rozwiązuje problem:

- (NSUInteger)countOfChildren{ 
     return [self.children count]; 
    } 

Dodanie zarówno robi nie rozwiązać problem w obu SDK. Przeciwnie, łamie to naprawę.

Czy ktoś ma pojęcie, dlaczego tak się dzieje i dlaczego istnieje różnica między nimi, mimo że nie ma wzmianki o zmianach w danych podstawowych lub fundacji między systemem iOS 6.0 a systemem iOS 6.1.

+0

Dodałeś to do 'NSManagedObjectModel'? Trudno zobaczyć, w jaki sposób można to skompilować, nie mówiąc już o pracy. 'NSManagedObjectModel' nie ma żadnych relacji z innymi klasami. –

+0

Przykro mi, mój zły, miałem na myśli podklasę NSManagedObject' również za drugim razem. Poprawiłem błąd. –

+0

Nie jest to rozwiązanie konkretnego problemu, ale inny pogląd na to: Co powiesz na pobieranie dzieci i liczenie liczby różnych rodziców? Może to [post] (http://stackoverflow.com/questions/9157436/distinct-count-via-core-data-nsexpression-int-nsfetchedresultscontroller) pomaga. – Paul

Odpowiedz

6

Myślę, że mówiąc "Keypath zawierający agregat KVC tam, gdzie nie powinno być, nie udało się obsłużyć dzieci. @ Count" Core Data chce powiedzieć, że nie obsługuje tego rodzaju deskryptora sortowania. Jest to bardzo prawdopodobne, ponieważ kiedy wspierający sklep SQLite odbiera żądanie pobierania, musi wygenerować SQL, który robi to, co opisuje żądanie pobierania. Przypadek "dzieci. @ Count" jest w rzeczywistości bardziej skomplikowany pod maską niż mogłoby się wydawać.

"Poprawka" z nadpisaniem -countOfChildren nie jest naprawą. Załóżmy na sekundę, że to rozwiązuje problem, a następnie -countOfChilden zostanie wywołany na każdym Rodzicu. Kiedy pierwszy raz uzyskujesz dostęp do self.children, Core Data musi wykonać zapytanie SQL, które określa (przynajmniej) klucze podstawowe dzieci, utworzy NSManagedObjectID, NSManagedObjects i zwróci wynik. Gdyby to zadziałało, zobaczyłbyś bardzo kiepską wydajność.

Istnieje kilka sposobów rozwiązania problemu.

1. Przechowywać liczyć dziecko w trwałym atrybutem

Wystarczy dodać atrybut (nazwa: cachedCountOfChildren, typ: Integer 64 bit) do jednostki dominującej. W warstwie kontrolera (NIE W SWOJEJ WARSTWIE MODELI) zwiększaj buforowaneCountOfChildren o 1 za każdym razem, gdy przypisujesz dziecko rodzicowi i zmniejszaj buforowaneCountOfChildren za każdym razem, gdy usuniesz dziecko z rodzica. Następnie użyj cachedCountOfChildren w kluczu deskryptora sortowania. Będzie to miało świetną wydajność.

2. Użyj słownika wyniki

Ustaw resultType swojego NSFetchRequest do NSDictionaryResultType. Spowoduje to wywołanie -executeFetchRequest: error: spowoduje zwrócenie NSDictionaries zamiast NSManagedObjects.NSFetchRequest z NSDictionaryResultType może robić różne rzeczy. Na przykład możesz użyć setPropertiesToGroupBy i NSExpression (...). Proszę spojrzeć na sesję WWDC "Używanie iCloud z danymi podstawowymi (2012)" (zaczynając od slajdu 122) w celach informacyjnych. Zasadniczo im pokazać, jak skonstruować wniosek, że zwróci tablicę, która zawiera słowniki, które mają tę strukturę:

(
{ 
    countOfChildren = 1; 
    parentName = "hello"; 
}, 
{ 
    countOfChildren = 134; 
    parentName = "dsdsd"; 
}, 
{ 
    countOfChildren = 2; 
    parentName = "sdd"; 
} 
) 

Jak widać dostaniesz niesegregowanych wynik z powrotem. Ale sortowanie tej tablicy przez countOfChildren może odbywać się w pamięci bardzo efektywnie. Wygenerowane dane SQL na podstawie danych podstawowych również będą w tym przypadku bardzo wydajne i będziesz w stanie dokładnie określić, które atrybuty powinny zawierać słowniki. Tak więc wynik powinien również być bardzo efektywny pod względem pamięci. To rozwiązanie ma tę zaletę, że nie musisz śledzić countOfChildren.

Musisz zdecydować, które rozwiązanie jest najlepsze dla Ciebie w zależności od Twojej sytuacji.

+0

Dlaczego nie zaimplementować tego w podklasie NSManagedObject (tj. W warstwie modelu)? –

+0

Zrealizować co? 1 lub 2? –

+0

Zastanawiam się, dlaczego napisałeś co następuje dla 1: 'W warstwie kontrolera (NIE W WARUNKU MODELU) zwiększaj buforowaneCountOfChildren o 1 za każdym razem, gdy ...". –