Cos'è un errore dei dati di base?

I guasti sono una componente essenziale dei Core Data. Anche se il termine sembra inquietante, i difetti sono inerenti al ciclo di vita di un record di dati di base. In questo tutorial, imparerai quali errori sono, come gestirli e come eseguire il debug dei problemi relativi agli errori.

Prerequisiti

Core Data è un argomento avanzato, quindi presumo che tu abbia già familiarità con lo sviluppo di Xcode e iOS. Anche se userò il linguaggio di programmazione Swift per questo tutorial, tutto in questo tutorial è applicabile anche a Objective-C.

Wat è un difetto?

Core Data è molto bravo in quello che fa grazie al duro lavoro del team Core Data di Apple. Core Data è altamente ottimizzato per mantenere basso il suo impatto sulla memoria senza sacrificare le prestazioni. Faulting è una delle tecniche utilizzate dai Core Data per consumare meno memoria possibile.

Faulting non è univoco per i Core Data. Una tecnica simile è utilizzata in molti altri framework, come Ember e Ruby on Rails. L'idea è semplice, carica solo i dati quando è necessario. Per far funzionare la faglia, Core Data crea un po 'di magia sotto la cappa creando sottoclassi personalizzate in fase di compilazione che rappresentano i guasti. Vediamo come funziona con un esempio.

Ho creato una semplice applicazione di esempio con cui lavorare. Scarica il progetto Xcode da GitHub e aprilo in Xcode. Il progetto utilizza Swift 2.1, il che significa che è necessario Xcode 7.1 o versioni successive per soddisfare il compilatore.

Il modello dati contiene due entità, ElencoArticolo. Un elenco può contenere zero o più elementi e un elemento è sempre collegato a un elenco. È un classico esempio di relazione uno-a-molti.

Eseguire l'applicazione e popolare l'archivio persistente, un database SQLite, con alcuni dati creando alcuni elenchi e voci. Esci dall'applicazione quando hai finito.

Difetti di cottura

Ora dovresti avere un'applicazione con alcuni dati. Vediamo come i guasti funzionano in pratica aggiungendo alcune dichiarazioni di stampa. Aperto ListsViewController.swift e cercare prepareForSegue (_: mittente :). In questo metodo, recuperiamo la lista che l'utente ha selezionato nella vista tabella. Decommentare le istruzioni di stampa in prepareForSegue (_: mittente :) come mostrato nella seguente implementazione.

override func prepareForSegue (segue: UIStoryboardSegue, mittente: AnyObject?) if follows.identifier == SegueListViewController guardia consente indexPath = tableView.indexPathForSelectedRow else return // Fetch List let list = self.fetchedResultsController.objectAtIndexPath (indexPath) as! Stampa lista ("1: \ (lista)") se let items = list.items print ("2: \ (items)") print ("3: \ (items.count)") print ("4: \ (articoli) ") se let item = items.anyObject () print (" 5: \ (item) ") print (" 6: \ (item.name) ") print (" 7: \ (item) ")  print ("8: \ (list)") // Recupera destinazione Visualizza controller lascia listViewController = segue.destinationViewController as! ListViewController // Configura il View Controller listViewController.list = list

Esegui l'applicazione e tocca uno degli elenchi nel controller di visualizzazione degli elenchi. L'esempio è interessante solo se si tocca un elenco con uno o più elementi associati. Ispezioniamo passo dopo passo l'output delle istruzioni di stampa.

1:  (entità: elenco; id: 0xd0000000001c0000  ; dati: items = ""; name =" List 6 ";)

La prima cosa da notare è il nome della classe, Faulting.List. Questo è ciò che ci aspettiamo dal momento che il modulo è chiamato faulting e la classe è nominata Elenco. In Swift, il nome completo della classe di una classe è costituito dal nome del modulo e dal nome della classe.

L'output nella console mostra anche il nome dell'entità, Elenco, e un dizionario di dati. Il nome l'attributo è impostato su Elenco 6 mentre il elementi l'attributo è contrassegnato come a rapporto colpa. Questo è il primo tipo di errore in Core Data. Core Data capisce che non è necessario caricare la relazione. Invece, segna la relazione come un difetto. La seconda affermazione stampa lo conferma come puoi vedere di seguito.

2: Errore 'articoli' della relazione sull'oggetto gestito (0x154e5d0b0)  (entità: elenco; id: 0xd0000000001c0000  ; dati: items = ""; name =" List 6 ";)

La terza dichiarazione di stampa, tuttavia, è più interessante. Stampa il numero di elementi associati all'elenco.

3: 2

I dati fondamentali possono farlo solo attivando l'errore di relazione. Richiede lo store permanente per gli elementi associati all'elenco. Questo, tuttavia, è solo una parte della storia, come illustrato dalla quarta dichiarazione di stampa.

4: relazione "articoli" sull'oggetto gestito (0x154e5d0b0)  (entità: elenco; id: 0xd0000000001c0000  ; data: items = ("0xd000000000540002 "," 0xd000000000580002 "); name =" List 6 ";) con oggetti (  (entità: articolo; id: 0xd000000000540002  ; dati: ),  (entità: articolo; id: 0xd000000000580002  ; dati: ))

Non vediamo più un errore di relazione, ma vediamo ancora un errore. Di cosa si tratta? I Dati principali possono darci solo il numero di elementi per l'elenco licenziando o risolvendo l'errore di relazione. Tuttavia, ciò non significa che Core Data risolva gli elementi della relazione. L'output nella console lo conferma.

Possiamo vedere che ci sono i record per gli articoli, incluso l'identificatore che i dati di base utilizzano internamente. Il dizionario dei dati, tuttavia, è contrassegnato come un errore. Ancora una volta, i dati di base ci forniscono solo ciò che chiediamo. Fortunatamente, i dettagli nitidi sono gestiti da Core Data.

Scaviamo un po 'più a fondo e recuperiamo uno degli elementi dall'elenco. Lo facciamo chiamando ANYOBJECT () sul elementi oggetto. Stampiamo l'articolo risultante nella quinta dichiarazione di stampa.

5:  (entità: articolo; id: 0xd000000000540002  ; dati: )

L'output non dovrebbe sorprendervi. L'output conferma che abbiamo a che fare con un Articolo entità. Non sorprende che il dizionario dei dati sia ancora contrassegnato come un errore. Nella sesta dichiarazione di stampa, stampiamo il nome attributo dell'articolo.

6: articolo 0

Poiché chiediamo il valore di un attributo del record, Core Data licenzia l'errore per darci quel valore. Raccoglie i dati dell'elemento e popola il dizionario dei dati. La settima dichiarazione di stampa conferma questi risultati.

7:  (entità: articolo; id: 0xd000000000540002  ; data: list = "0xd0000000001c0000 "; name =" Articolo 0 ";)

Il dizionario dei dati contiene il nome attributo così come il elenco relazione. L'ottava e ultima dichiarazione di stampa mostra che il difetto di relazione del elenco l'oggetto è risolto.

8:  (entità: elenco; id: 0xd0000000001c0000  ; data: items = ("0xd000000000540002 "," 0xd000000000580002 "); name =" List 6 ";)

Incapace di soddisfare un errore

Ho deciso di scrivere questo articolo per spiegare un problema che molti sviluppatori che utilizzano Core Data eseguono in un punto o in un altro, sparando un errore che non può essere soddisfatto. Il problema è semplice Supponiamo che tu abbia una lista con un numero di elementi e, ad un certo punto, l'utente cancella la lista. Cosa succede agli oggetti di quella lista? Cosa succede se Core Data tenta di sparare a elenco relazione di uno degli elementi che appartenevano a quella lista? Scopriamolo.

Rivediamo il progetto che hai scaricato da GitHub. Aperto Faulting.xcdatamodeld, il modello di dati del progetto. Seleziona il elementi relazione del Elenco entità e aprire il Data Model Inspector sulla destra. Quello che ci interessa è il Elimina regola, che è attualmente impostato su Cascata. Ciò significa che ogni elemento della lista viene cancellato quando l'elenco viene cancellato. Questo ha senso dal momento che non vogliamo avere oggetti abbandonati che non sono associati a una lista.

Seleziona il elenco relazione del Articolo entità e aprire il Data Model Inspector. Il Elimina regola di questa relazione è impostata su annullare. Ciò significa che la destinazione della relazione, l'oggetto elenco, è impostata su null quando il record di destinazione, l'elemento, viene cancellato. Questa è la regola di cancellazione predefinita per una relazione.

Esegui l'applicazione, crea alcuni elenchi e elementi e tocca Elementi in alto a sinistra per mostrare tutti gli oggetti che hai creato. Rubinetto Annulla in alto a sinistra, elimina uno degli elenchi e tocca il Elementi pulsante di nuovo per vedere cosa è cambiato. Come previsto, anche gli elementi associati all'elenco cancellato sono stati cancellati da Core Data. Questa non è magia. Core Data esegue semplicemente le regole di cancellazione che abbiamo definito nel modello di dati.

Possiamo concludere che le relazioni sono impostate correttamente per questo tipo di applicazione. Ma cosa succede se non configuriamo correttamente le regole di cancellazione. Aprire il modello dati e impostare la regola di cancellazione di entrambe le relazioni, elementielenco, a Nessuna azione. Avvia di nuovo l'applicazione e crea alcuni elenchi e voci. Se elimini un elenco e tocchi il Elementi pulsante in alto a sinistra per vedere ogni elemento nell'archivio permanente, gli elementi associati all'elenco cancellato dovrebbero essere ancora lì. Non sono stati eliminati anche se la lista a cui appartengono è stata rimossa.

Tocca uno degli elenchi e guarda cosa succede. Se stai eseguendo l'applicazione su iOS 8, l'applicazione si bloccherà a causa di un'eccezione non rilevata. Se stai eseguendo l'applicazione su iOS 9, vedrai solo un errore nella console. Smetti di grattarti la testa e scopriamolo insieme.

Oggetto inaccessibile

Anche se l'applicazione si blocca su iOS 8, l'output che vediamo nella console è chiaro e preciso.

Faulting [7189: 2427906] *** Termina l'applicazione a causa dell'eccezione non rilevata 'NSObjectInaccessibleException', motivo: 'CoreData non è riuscito a soddisfare un errore per' 0x175b2ba0 "

La prima cosa da notare è che l'applicazione si è bloccata a causa di un'eccezione non rilevata. Ciò che è più importante per la nostra discussione, tuttavia, è la ragione per cui è stata generata l'eccezione. Core Data ci dice che non è stato in grado di soddisfare un errore per una relazione. La relazione cui si riferisce Core Data è la elenco relazione dell'oggetto che abbiamo sfruttato nella vista tabella.

Se guardi all'implementazione di tableView (tableView: didSelectRowAtIndexPath :), allora capirai perché i Core Data hanno lanciato un'eccezione. In questo metodo, recuperiamo l'elemento toccato dall'utente e stampiamo il nome dell'elenco sulla console.

func tableView (tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) tableView.deselectRowAtIndexPath (indexPath, animato: true) se let item = self.fetchedResultsController.objectAtIndexPath (indexPath) as? Articolo stampa (item.list? .Name)

Perché il elenco la relazione è un difetto, Core Data ha bisogno di sparare l'errore per risolverlo. Ciò significa che Core Data richiede all'archivio permanente il record dell'elenco. Il problema è che il record non si trova più nell'archivio permanente, motivo per cui è stata generata un'eccezione.

Elimina errori inaccessibili

Fino a iOS 9, questo è sempre stato il comportamento predefinito. A partire da iOS 9, Core Data non getta più un'eccezione. Invece, registra un messaggio criptico sulla console. Nonostante il messaggio non sia chiaro, contiene ancora la ragione del problema.

Faulting [2806: 1306995] CoreData: avviso: un delegato NSManagedObjectContext ha annullato il comportamento di gestione degli errori per eliminare in modo silenzioso l'oggetto con ID '0xd000000000140000 'e sostituisce nil / 0 per tutti i valori di proprietà invece di lanciare.

L'essenza è che Core Data non getta più un'eccezione quando non è in grado di soddisfare un errore. La buona notizia è che l'applicazione non si blocca più se non si rileva l'eccezione.

La ragione per non lanciare un'eccezione su iOS 9 è dovuta all'introduzione di una nuova proprietà, shouldDeleteInaccessibleFaults, e un nuovo metodo, shouldHandleInaccessibleFault (_: forObjectID: triggeredByProperty :), sul NSManagedObjectContext classe. Non copro queste aggiunte in questo tutorial.

Quello che devi ricordare è che iOS 9, per impostazione predefinita, imposta il shouldDeleteInaccessibleFaults a vero. Ciò significa che un oggetto gestito inaccessibile è contrassegnato come cancellato e le sue proprietà vengono azzerate. Se shouldDeleteInaccessibleFaults è impostato per falso, Core Data ripristina il vecchio comportamento generando un'eccezione.

Certo, il shouldDeleteInaccessibleFaults proprietà va di pari passo con shouldHandleInaccessibleFault (_: forObjectID: triggeredByProperty :). Se si sostituisce questo metodo, è possibile gestire gli oggetti inaccessibili in modo più elegante.

Gestione dei guasti

Quando incontri un'eccezione dovuta al fatto che Core Data non è in grado di soddisfare un errore, puoi pensare che Core Data sia un po 'aggressivo. Questa reazione è abbastanza comune per le persone nuove al framework. La verità è che lo sviluppatore è in colpa. I dati di base sono stati progettati tenendo conto di una serie specifica di obiettivi e l'errore è una componente essenziale per realizzare questi obiettivi.

Nonostante il loro nome, i difetti dovrebbero sempre essere soddisfatti. Se un errore non può essere soddisfatto, significa che il modello di dati non è impostato correttamente o l'applicazione non rispetta le regole stabilite dalla struttura Core Data.

Nella Guida alla programmazione dei dati di base, Apple elenca una serie di scenari che possono portare a guasti che non possono più essere soddisfatti. Gli scenari più comuni sono le relazioni configurate in modo errato (eliminare le regole) e l'eliminazione di un oggetto gestito mentre l'applicazione ha ancora un forte riferimento a quell'oggetto gestito.

Stai attento, ci sono altri possibili scenari. Dalla mia esperienza, gli sviluppatori si imbattono spesso in problemi simili a causa di un problema con lo stack di Core Data. Ad esempio, se l'applicazione mantiene un forte riferimento a un oggetto gestito e, ad un certo punto, rilascia il contesto dell'oggetto gestito dell'oggetto gestito, quindi Core Data non è più in grado di soddisfare eventuali errori per tale oggetto gestito. Spero tu capisca che questo non dovrebbe accadere se giochi secondo le regole di Core Data.

Conclusione

Gli errori di Core Data sono incredibilmente utili e un componente chiave del framework di persistenza di Apple. Spero che questo articolo ti abbia insegnato come affrontare i difetti e dove cercare se incontri difetti che non possono essere soddisfatti. Se hai qualche domanda, sentiti libero di lasciarli nei commenti qui sotto o di contattarmi su Twitter.