7

È un problema di lunga data quando si utilizzano i dati principali per molte relazioni che è molto difficile ordinare una richiesta di recupero utilizzando NSSortDescriptor su un'entità Parent in base al numero il numero di children è in una relazione uno-a-molti con un'entità Child. Ciò è particolarmente utile in combinazione con uno NSFetchedResultsController. In genere l'inizializzazione del descrittore tipo come:NSSortDescriptor per ordinare in base al numero di elementi in molte relazioni tra core Data

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

risultati in un'eccezione 'Keypath containing KVC aggregate where there shouldn't be one; failed to handle [email protected]

Su iOS 6.1, ho scoperto una correzione aggiungendo la funzione di accesso KVO -countOf<Key> come attributo al mio modello di oggetto gestito come un intero genere. Non ho implementato nulla per questo attributo nella mia sottoclasse NSManagedObject, poiché tutta la magia sembra avvenire sotto il cofano. (vedi https://stackoverflow.com/a/15546371/2042527).

Tuttavia, questo non funziona su iOS 6.0. Qui ho trovato che l'aggiunta del seguente metodo per il vostro NSManagedObject sottoclasse risolve il problema:

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

aggiungendo sia non non risolvere il problema in entrambi SDK. Al contrario, rompe la correzione.

Qualcuno ha idea del perché questo sta accadendo e perché c'è una differenza tra entrambi, anche se non si fa menzione delle modifiche a Core Data o Foundation tra iOS 6.0 e iOS 6.1.

+0

Hai aggiunto questo a "NSManagedObjectModel'? È difficile vedere come questo possa anche essere compilato, per non parlare del lavoro. 'NSManagedObjectModel' non ha alcuna relazione con altre classi. –

+0

Mi dispiace, mio ​​male, intendevo anche la sottoclasse 'NSManagedObject' anche la seconda volta. Ho corretto l'errore. –

+0

Non una soluzione al tuo problema esatto ma un'altra vista su di esso: che ne dici di andare a prendere i bambini e contare il numero di genitori distinti? Forse questo [post] (http://stackoverflow.com/questions/9157436/distinct-count-via-core-data-nsexpression-into-nsfetchedresultscontroller) ti aiuta. – Paul

risposta

6

Penso che dicendo "Keypath che contiene aggregato KVC dove non dovrebbe essercene uno, non è riuscito a gestire i bambini. @ Count" Core Data vuole dirti che non supporta questo tipo di descrittore di ordinamento. Ciò è molto probabile perché quando l'archivio SQLite di supporto riceve la richiesta di recupero, deve generare SQL che esegue ciò che descrive la richiesta di recupero. Il caso di "children. @ Count" è in realtà più complesso di quanto si possa pensare.

Il "fix" con overriding -countOfChildren non è una vera soluzione. Supponiamo per un secondo che questo risolva il problema, quindi il conteggioOfChilden verrebbe chiamato su ogni Parent. Quando accedi per la prima volta a self.children, Core Data deve eseguire una query SQL che determina (almeno) le chiavi primarie dei bambini, creare NSManagedObjectIDs, NSManagedObjects e restituire il risultato. Se funzionasse, vedresti prestazioni pessime.

Esistono diverse soluzioni al problema.

1. Conservare il bambino conta in un attributo persistente

Basta aggiungere un attributo (nome: cachedCountOfChildren, Tipo: intero a 64 bit) al vostro soggetto controllante. Nel livello controller (NON NEL TUO MODELLO DI LIVELLO) incrementa cachedCountOfChildren di 1 ogni volta che assegni un figlio a un genitore e decrementi cachedCountOfChildren ogni volta che rimuovi un figlio da un genitore. Quindi si utilizza cachedCountOfChildren nella chiave del descrittore di ordinamento. Questo avrà grandi prestazioni.

2. dizionario utilizzare i risultati

Impostare la resultType del NSFetchRequest a NSDictionaryResultType. Ciò causerà -executeFetchRequest: error: per restituire NSDictionaries invece di NSManagedObjects.Un NSFetchRequest con un NSDictionaryResultType può fare cose diverse. Ad esempio, è possibile utilizzare setPropertiesToGroupBy e NSExpression (...). Per riferimento, consultare la sessione WWDC "Uso di iCloud with Core Data (2012)" (a partire dalla diapositiva 122). In sostanza vi mostrano come costruire una richiesta che restituirà una matrice che contiene dizionari che hanno questa struttura:

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

Come si può vedere si otterrà un risultato non differenziati indietro. Ma ordinare questo array per countOfChildren può essere fatto in memoria in modo molto efficiente. Anche l'SQL generato da Core Data sarà molto efficiente in questo caso e sarai in grado di specificare esattamente quali attributi devono essere contenuti nei dizionari. Quindi il risultato dovrebbe anche essere molto efficiente in termini di memoria. Questa soluzione ha il vantaggio che non devi tenere traccia dei countOfChildren.

Devi decidere quale soluzione è meglio per te in base alla situazione.

+0

Perché non implementarlo su una sottoclasse NSManagedObject (vale a dire il livello del modello)? –

+0

Implementare cosa? 1 o 2? –

+0

Mi chiedo perché hai scritto quanto segue per 1: 'Nel tuo livello controller (NON NEL TUO LIVELLO MODELLO) incrementa cachedCountOfChildren di 1 ogni volta che ...'. –