iPhone Core Data i tuoi primi passi

Core Data è un framework che Apple fornisce agli sviluppatori che è descritto come un "framework grafico per la gestione degli oggetti e il framework di persistenza". Cosa significa in realtà? Il framework gestisce dove vengono archiviati i dati, come vengono archiviati, la memorizzazione nella cache dei dati e la gestione della memoria. È stato trasferito su iPhone da Mac OS X con la versione 3.0 dell'iPhone SDK.

In questo tutorial ti guiderò attraverso il processo di creazione di un progetto con Core Data e ti mostrerò come utilizzarlo con un semplice UITableViewController. Sarà un semplice database di una tabella che memorizzerà i tempi di giro e mostrerà i tempi e le loro differenze di divisione in un UITableView.

Perché utilizzare i dati principali?

Prima di iniziare, è importante capire perché è possibile utilizzare Core Data con SQLite per l'archiviazione su elenchi di proprietà, un formato XML personalizzato o l'accesso diretto al database SQLite. L'API Core Data consente agli sviluppatori di creare e utilizzare un database relazionale, eseguire la convalida dei record ed eseguire query utilizzando condizioni senza SQL. Essenzialmente ti permette di interagire con SQLite in Objective-C e non devi preoccuparti delle connessioni o della gestione dello schema del database, e molte di queste funzionalità sono familiari alle persone che hanno utilizzato tecnologie ORM (Object-Relational Mapping) come quelle implementate in Ruby su Rails, CakePHP, LINQ o altre librerie e framework che estendono l'accesso al database. Il vantaggio principale di questo approccio è che elimina il tempo di sviluppo altrimenti necessario per scrivere query SQL complesse e gestire manualmente l'SQL e l'output di tali operazioni.

Creazione di un nuovo progetto iPhone con Core Data

L'applicazione di esempio che costruiremo oggi è un semplice cronometro. Creerà un nuovo record nell'archivio dei Core Data per ogni giro che realizziamo. UITableView mostrerà quindi la differenza tra l'attuale e gli ultimi giri.

Per iniziare apriremo XCode e creeremo un nuovo progetto. Chiamalo come vuoi, l'ho chiamato "LapTimer". Creeremo una "Applicazione basata su finestre" dalla selezione del modello Nuovo progetto. Assicurati che "Usa i dati di base per l'archiviazione" sia selezionato.

Il progetto dovrebbe essere familiare a ciò che potresti aver visto prima se hai già sviluppato applicazioni per iPhone.

Impostazione della struttura dei dati principali

Non c'è molta impostazione da fare in quanto l'opzione "Usa dati di base per l'archiviazione" nel modello "Applicazione basata su finestra" ha impostato automaticamente alcune variabili importanti e file creati nel progetto per noi.

Il file LapTimer.xcdatamodel è dove definiremo lo schema per il nostro database SQLite. Il framework Core Data è stato inoltre aggiunto al progetto per includere i file per l'accesso API. Le altre modifiche vengono apportate all'interno dei file dell'applicazione predefiniti. In particolare, i file delegati dell'applicazione hanno i metodi per impostare l'archivio Dati principali nella nostra applicazione e farvi riferimento in UIViewControllers figlio..

Quello che ci interessa al momento è il file LapTimer.xcdatamodel sotto "Risorse". Questo file ti fornisce una mappa visiva del tuo schema che mostra entità e attributi.

Ci sono alcuni termini e frasi diversi utilizzati nei dati di base che possono essere liberamente collegati ai nomi di database regolari, ma non sono identici.

Una "entità", anche nota come "oggetto gestito", è simile a una tabella. È una definizione di un oggetto che conterrà una raccolta di dati. Un oggetto entità contiene "attributi". Questi possono essere liberamente associati alle colonne, ma questi attributi non sono solo limitati all'archiviazione dei dati. Gli attributi possono definire una relazione tra due entità, una proprietà caricata dinamicamente o definire una proprietà per l'archiviazione dei dati.

Dal diagramma sopra, puoi avere un'idea di quanto siano flessibili gli oggetti derivati ​​dalle entità Core Data. Per questa applicazione di esempio avremo bisogno di un'entità davvero semplice. Chiameremo l'entità "Event" che memorizzerà i record dei nostri giri.

Quello che faremo per creare questa entità è fare clic sul pulsante [+] nella prima colonna (dalla sinistra) in alto. Ciò creerà una nuova entità nell'elenco e visivamente sulla mappa dello schema sotto le colonne.

Questo è un bel editor visuale per il tuo modello. Core Data fa davvero il lavoro pesante quando si tratta della parte "M" (Modello) di MVC. L'editor visivo mostra le relazioni, le proprietà e le entità del negozio mentre lo schema viene creato e gestito in modo dinamico per te. Questo è simile a Interface Builder poiché si occupa di allocare, gestire e posizionare oggetti per te su un UIView senza una singola riga di codice.

Con la nuova entità Event sul posto, vogliamo creare una "proprietà". Poiché questa proprietà memorizzerà i dati, verrà impostata come "attributo". Quindi questo nuovo attributo memorizzerà la data corrente per quando è stato creato il record. Nella nostra applicazione di esempio useremo questo per fare riferimento ai nostri tempi sul giro.

La prossima colonna a destra è dove definiamo le nostre proprietà (assicurati che l'entità Event sia selezionata). Quindi, crea una nuova proprietà usando il pulsante [+] nella colonna, seleziona "Aggiungi attributo".

Chiameremo l'attributo "timeStamp" e imposteremo il suo Type su "Date". Nel menu a discesa Seleziona tipo ci sono tipi di dati di colonna simili a quelli disponibili in sistemi di database relazionali come PostgreSQL o MySQL, inclusi tipi di dati come numeri interi, float, stringhe, booleani, date e dati binari (blob).

Per questo attributo, non abbiamo bisogno di altre opzioni selezionate o modificate.

Questo è per il file xcdatamodel e possiamo passare all'integrazione nella nostra app. Ora è un buon momento per salvare il tuo lavoro.

Creare i nostri file di modello

Se hai utilizzato un framework MVC che ha definizioni di modelli di database che definiscono la struttura o i comportamenti di una tabella, allora questo sarà un compito familiare.

Dobbiamo iniziare creando una definizione dell'entità. Facciamo ciò creando una classe NSManagedObject dell'entità e definendo le variabili del negozio.

Questo è un processo semplice. Seleziona l'entità Event nel file LapTimer.xcdatamodel e vai su File> Nuovo file. Vedrai che c'è un nuovo modello di file nella sezione "Cocoa Touch Class" chiamata "Managed Object Class".

Seleziona il modello e premi "Avanti". La schermata successiva sta semplicemente definendo dove salviamo il file e il target per includerlo, tutto ciò è corretto per impostazione predefinita, quindi premi nuovamente "Avanti". La schermata successiva è dove si definiscono le entità per cui si desidera creare classi NSManagedObject. Se non è selezionato, seleziona Evento e assicurati che le caselle di controllo "Genera Accessors" e "Genera proprietà Obj-C 2.0" siano selezionate, al momento non abbiamo bisogno di convalide. Hit Finish.

Quindi ora abbiamo 2 nuovi file nella nostra applicazione. Event.m ed Event.h. Definiscono la classe NSManagedObject per l'entità Evento che abbiamo creato nel nostro archivio Dati principali. Definiscono il campo timeStamp così quando vogliamo usare la classe Event possiamo accedere all'attributo.

Event.h

 #importare  @interface Event: NSManagedObject  @property (nonatomic, retain) NSDate * timeStamp; @fine 

Event.m

 #import "Event.h" @implementation Event @dynamic timeStamp; @fine 

Come le definizioni del modello in altri framework e linguaggi, puoi aggiungere metodi personalizzati per tutti i record nell'entità Event. Noterai che l'attributo timeStamp è stato aggiunto e assegnato come oggetto NSDate.

Core Data e KVC

Con l'installazione di Core Data è ora di lavorare su alcune delle logiche del controller nell'applicazione. Useremo un UITableViewController come interfaccia principale dell'app per mostrare i tempi sul giro e registrare una nuova ora.

Quindi, creeremo il nuovo UITableViewController con File> Nuovo file. Quindi sotto la sezione iPhone seleziona "sottoclasse UIViewController" e seleziona "Sottoclasse UITableViewController" ma non le caselle di controllo relative all'utilizzo di XIB o per il targeting per un iPad. Non useremo un XIB per questo controller. Chiama il nuovo file "TimeTableController" e completa la procedura guidata.

In questo controller saranno necessarie 2 proprietà, un riferimento a NSManagedObjectContext e una matrice per memorizzare i record in per UITableView. Oltre a definire queste proprietà, importeremo il file Event.h in modo che possiamo usare la classe.

TimeTableController.h

 #importare  #import "Event.h" @interface TimeTableController: UITableViewController NSManagedObjectContext * managedObjectContext; NSMutableArray * eventArray;  @property (nonatomic, retain) NSManagedObjectContext * managedObjectContext; @property (nonatomic, retain) NSMutableArray * eventArray; - (void) fetchRecords; - (void) addTime: (id) mittente; @fine 

Cos'è un NSManagedObjectContext? Si chiama "scratch pad" per Core Data nell'applicazione per la gestione del recupero, dell'aggiornamento e della creazione di record nello store. Gestisce anche alcune funzionalità fondamentali dei dati principali, tra cui convalide e gestione degli annullamenti / ripristini per i record.

Il contesto dell'oggetto gestito è la connessione tra il codice e l'archivio dati. Tutte le operazioni che eseguirai per i Core Data lo faranno rispetto al contesto dell'oggetto gestito. Quando viene eseguita una richiesta, il contesto dell'oggetto gestito parlerà con il coordinatore dell'archivio permanente, responsabile della mappatura degli oggetti ai dati per l'archivio dati. Ciò consente ai dati principali di essere flessibili tra diversi formati di archivio dati. Ecco un diagramma di come appare.

Con il file di intestazione definito, è necessario propagare le proprietà e i metodi allocati nel file di implementazione.

TimeTableController.m

 #import "TimeTableController.h" @implementation TimeTableController @synthesize managedObjectContext, eventArray; // ... // ... codice commentato predefinito dal modello di file // ... - (void) viewDidLoad [super viewDidLoad]; self.title = @ "Lap Times"; UIBarButtonItem * addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem: UIBarButtonSystemItemAdd target: self action: @selector (addTime :)]; self.navigationItem.rightBarButtonItem = addButton; [addButton release]; [self fetchRecords];  - (void) addTime: (id) sender Event * event = (Event *) [NSEntityDescription insertNewObjectForEntityForName: @ "Event" inManagedObjectContext: managedObjectContext]; [evento setTimeStamp: [data NSDate]]; Errore NSError *; if (! [managedObjectContext save: & error]) // Questo è un errore grave che dice che il record // non può essere salvato. Consigliare all'utente di // riprovare o riavviare l'applicazione.  [eventArray insertObject: event atIndex: 0]; [self.tableView reloadData];  - (void) fetchRecords // Definire la nostra tabella / entità per utilizzare NSEntityDescription * entity = [NSEntityDescription entityForName: @ "Event" inManagedObjectContext: managedObjectContext]; // Imposta la richiesta di recupero NSFetchRequest * request = [[NSFetchRequest alloc] init]; [request setEntity: entity]; // Definire il modo in cui ordinare i record NSSortDescriptor * sortDescriptor = [[NSSortDescriptor alloc] initWithKey: @ "timeStamp" ascendente: NO]; NSArray * sortDescriptors = [NSArray arrayWithObject: sortDescriptor]; [request setSortDescriptors: sortDescriptors]; [versione di sortDescriptor]; // Recupera i record e gestisce un errore NSError * error; NSMutableArray * mutableFetchResults = [[managedObjectContext executeFetchRequest: request error: & error] mutableCopy]; if (! mutableFetchResults) // Gestire l'errore. // Questo è un errore grave e dovrebbe consigliare all'utente di riavviare l'applicazione // Salvare i dati recuperati in un array [self setEventArray: mutableFetchResults]; [versione mutableFetchResults]; [richiesta di rilascio];  // ... // ... altri commenti ai modelli e definizioni dei metodi predefiniti // ... - (void) dealloc [managedObjectContext release]; [versione eventArray]; [super dealloc];  @fine 

Questo è un bel po 'di codice, quindi lascialo passare attraverso ogni metodo individualmente. C'è un codice da quando crei il file dal modello, commentato metodi come viewDidUnload, che ho appena escluso da quanto sopra.

viewDidLoad

Iniziamo con la solita chiamata alla super classe. Quindi definiamo il titolo di UINavigationBar. Dopodiché è necessario definire il pulsante che useremo per aggiungere un record all'archivio dei dati principali. Quando viene premuto il pulsante, diciamo di chiamare il selettore addTime: che quindi interagirà con Core Data. Dopo i rilasci richiesti arriveremo a chiamare la funzione fetchRecords che è spiegata di seguito.

SommaTempi: (id) sender

Questo viene generato quando viene premuto UIBarButtonItem nella parte superiore destra di UINavigationBar. Dobbiamo creare un nuovo record di evento con l'attuale NSDate e salvarlo nel database.

Creiamo un nuovo record di evento chiamato NSEntityDescription. Questa è la tua riga nel database per il nuovo record. Per fare ciò definiamo il nome dell'entità cui appartiene il record e forniamo NSManagedObjectContext. La data corrente viene quindi impostata rispetto all'attributo timeStamp.

Viene quindi eseguita l'operazione di salvataggio, ma esiste una disposizione per gestire un errore in caso di errore dell'inserto. Di solito lanci un UIAlertView che dice che il record non è stato creato e forse chiedi all'utente di provare di nuovo o chiudere e riaprire l'applicazione.

Il record deve essere aggiunto alla matrice da cui UITableView alimenta. Quindi UITableView deve essere informato di ricaricare i dati. Puoi farlo più graficamente con un'animazione, ma per il tutorial, facciamo in modo che sia semplice.

fetchData

Questo metodo otterrà i dati dallo store e lo aggiungerà all'array che abbiamo nel controller. Per ulteriori dettagli su come ottenere i record, diamo un'occhiata a NSFetchRequest.

Recupero di dati dall'archivio dati

I dati principali hanno un approccio diverso nel recupero dei dati dal suo database. Si tratta di un archivio dati NoSQL, nel senso che tutte le condizioni di una query sono basate su metodi. Questo è ottimo in quanto l'archivio base, che è SQLite, può essere cambiato con qualsiasi altra tecnologia di database e l'unica cosa da cambiare sarebbe la connessione e i driver per il database.

Quindi, per creare una richiesta, creiamo un oggetto NSFetchRequest. Questo è l'oggetto base con cui verranno impostate le condizioni della query. Possiamo definire le condizioni per l'abbinamento di una particolare proprietà in base al modo in cui i record sono ordinati. Puoi trovare ulteriori informazioni su Core Data e le sue condizioni per NSFetchRequests nella loro documentazione.

La creazione di una nuova richiesta NSFetch è semplice. Hai solo bisogno di definire l'entità da cui vuoi i record e NSManagedObjectContext.

 NSEntityDescription * entity = [NSEntityDescription entityForName: @ "Event" inManagedObjectContext: managedObjectContext]; NSFetchRequest * request = [[NSFetchRequest alloc] init]; [request setEntity: entity]; 

L'entità viene definita utilizzando un oggetto NSEntityDescription che richiede il nome dell'entità e NSManagedObjectContext. La richiesta di recupero viene quindi creata passando la descrizione dell'entità. Questo equivarrebbe alla prima parte di un'istruzione SQL:

 SELEZIONA * DA 'eventi' 

Nella nostra applicazione di esempio ordiniamo i dati in base al timeStamp in modo discendente. Per fare ciò, usiamo un NSSortDescriptor.

 NSSortDescriptor * sortDescriptor = [[NSSortDescriptor alloc] initWithKey: @ "timeStamp" ascendente: NO]; NSArray * sortDescriptors = [NSArray arrayWithObject: sortDescriptor]; [request setSortDescriptors: sortDescriptors]; [versione di sortDescriptor]; 

Il NSSortDescriptor è stato creato e definiamo l'attributo che desideriamo ordinare e che sia crescente, in questo caso vogliamo che scenda in modo che sia impostato su NO. La richiesta di recupero può richiedere molti descrittori di ordinamento in modo che accetti un array quando imposta i descrittori di ordinamento. Dal momento che ne vogliamo uno solo, abbiamo solo bisogno di creare un array con un oggetto in esso. Impostiamo la matrice descrittore contro la richiesta di recupero e questo è quanto.

Per definire una condizione per abbinare i contenuti di un record, entra in gioco la classe NSPredicate. Permette la corrispondenza della richiesta di recupero o definisce un intervallo che il contenuto di un record deve soddisfare. Questo è l'equivalente ai tuoi pari, maggiore e minore delle corrispondenze in SQL. Ha più delle funzioni SQL di base, che puoi vedere qui.

L'impostazione di un predicato può essere molto semplice.

 NSPredicate * predicate = [NSPredicate predicateWithFormat: @ "(lastname come% @) AND (compleanno>% @)", lastNameSearchString, birthdaySearchDate]; 

Utilizzo del predicato NSPredicateWithFormat: è un metodo semplice e familiare che consente di definire le condizioni della query. Per una spiegazione approfondita su NSPredicates, la Documentazione Apple ha delle ottime guide.

Dopo aver definito le condizioni nella richiesta di recupero, è possibile eseguirlo.

 NSMutableArray * mutableFetchResults = [[managedObjectContext executeFetchRequest: request error: & error] mutableCopy]; 

Ciò restituirà una serie di oggetti entità, NSManagedObjects, da utilizzare nell'output dei dati.

Popolazione di UITableView da Core Data

Con i dati recuperati da Core Data e archiviati in eventArray, ora possiamo inviare tali record a UITableView.

La prima cosa è dire al tavolo che avremo bisogno solo di una sezione e di quante righe dobbiamo usare.

Estratto da TimeTableController.m

 -(NSInteger) numberOfSectionsInTableView: (UITableView *) tableView return 1;  - (NSInteger) tableView: (UITableView *) tableView numberOfRowsInSection: (NSInteger) section return [eventArray count];  

Se hai già utilizzato un UITableViewController, la prossima funzione dovrebbe essere diretta.

 -(UITableViewCell *) tableView: (UITableView *) tableView cellForRowAtIndexPath: (NSIndexPath *) indexPath static NSString * CellIdentifier = @ "Cell"; static NSDateFormatter * dateFormatter = nil; if (dateFormatter == nil) dateFormatter = [[NSDateFormatter alloc] init]; [dateFormatter setDateFormat: @ "h: mm.ss a"];  UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier: CellIdentifier]; if (cell == nil) cell = [[[UITableViewCell alloc] initWithStyle: UITableViewCellStyleValue1 reuseIdentifier: CellIdentifier] autorelease];  Event * event = [eventArray objectAtIndex: [indexPath row]]; Evento * previousEvent = nil; if ([eventArray count]> ([indexPath row] + 1)) previousEvent = [eventArray objectAtIndex: ([indexPath row] + 1)];  [cell.textLabel setText: [dateFormatter stringFromDate: [event timeStamp]]]; if (previousEvent) NSTimeInterval timeDifference = [[time timeStamp] timeIntervalSinceDate: [previousEvent timeStamp]]; [cell.detailTextLabel setText: [NSString stringWithFormat: @ "+%. 02f sec", timeDifference]];  else [cell.detailTextLabel setText: @ "---"];  restituisci cella;  

La cella mostrerà 2 valori, dall'uso dello stile UITableViewCellStyleValue1. La parte sinistra sarà il tempo del giro e la destra sarà la differenza in secondi rispetto al record precedente.

Poiché questo metodo itera, dobbiamo prestare particolare attenzione al carico in cui può mettere il dispositivo se non è gestito correttamente. Per questo motivo NSDatFormatter viene memorizzato come variabile statica in modo che possa essere riutilizzato in ogni iterazione senza allocarlo e rilasciarlo ogni volta.

Caricamento pigro

Lazy Loading è una tecnica in cui si ritarda il più possibile la richiesta o l'assegnazione di una proprietà. Ciò aiuta a mantenere la memoria bassa e durante le funzioni iterative questo è fondamentale. Sapere quando e come allocare i dati è fondamentale per mantenere un'app mobile veloce. La deallocazione dell'oggetto è altrettanto importante, quanto prima è, meglio è.

cellForRowAtIndexPath: è un metodo iterativo e tutti i dati elaborati o allocati in questo metodo devono essere mantenuti al minimo. Questo metodo viene eseguito ogni volta che una cella viene visualizzata, quindi quando un utente sta scorrendo rapidamente questo particolare metodo, in base alla dimensione del set di record, può essere chiamato molto frequentemente in successione.

L'attività successiva è ottenere l'oggetto evento associato alla riga della tabella che deve essere sottoposta a rendering. Dato che abbiamo bisogno di ottenere il record precedente per il confronto temporale, c'è un semplice controllo per vedere se c'è un record precedente e per memorizzarlo in previousEvent. Se esiste l'evento precedente, calcoliamo la suddivisione usando [NSDate timeIntervalSinceDate: (NSDate)]. TextLabel e detailedTextLabel vengono quindi impostati con i valori che abbiamo calcolato.

Finire l'applicazione

Con la configurazione di UITableViewController e l'origine dati della tabella che lavora con l'archivio di Core Data, tutto ciò che è necessario è caricare il controller all'avvio dell'applicazione.

Nel controller dell'applicazione è necessario definire una proprietà UINavigationController. Quindi il metodo applicationDidFinishLaunching ha solo bisogno di allocare il controller e abbiamo finito.

LapTimerAppDelegate.h

 @interface LapTimerAppDelegate: NSObject  NSManagedObjectModel * managedObjectModel; NSManagedObjectContext * managedObjectContext; NSPersistentStoreCoordinator * persistentStoreCoordinator; Finestra UIWindow *; UINavigationController * navigationController;  @property (nonatomico, retain, readonly) NSManagedObjectModel * managedObjectModel; @property (nonatomico, retain, readonly) NSManagedObjectContext * managedObjectContext; @property (nonatomico, retain, readonly) NSPersistentStoreCoordinator * persistentStoreCoordinator; @property (nonatomic, retain) Finestra IBOutlet UIWindow *; @property (nonatomic, retain) UINavigationController * navigationController; - (NSString *) applicationDocumentsDirectory; @fine 

Estratto da LapTimerAppDelegate.m

 #import "LapTimerAppDelegate.h" #import "TimeTableController.h" @implementation LapTimerAppDelegate @synthesize window, navigationController; - (void) applicationDidFinishLaunching: (applicazione UIApplication *) TimeTableController * tableController = [[TimeTableController alloc] initWithStyle: UITableViewStylePlain]; tableController.managedObjectContext = [self managedObjectContext]; self.navigationController = [[UINavigationController alloc] initWithRootViewController: tableController]; [versione tableController]; [window addSubview: [self.navigationController view]]; [window makeKeyAndVisible];  // ... // ... altri metodi template // ... - (void) dealloc [managedObjectContext release]; [versione di ManagedObjectModel]; [versione persistentStoreCoordinator]; [rilascio finestra]; [versione NavigationController]; [super dealloc];  

Il file TimeTableController.h è incluso nel delegato dell'applicazione e quindi allocato con un UINavigationController.

Questo dovrebbe essere. Costruisci l'applicazione per verificare la presenza di errori. Alcuni degli esempi di codice sono stati solo estratti, nessuno del codice generato durante la creazione di un file è stato rimosso, solo compilato. Se si incontrano errori che non si possono incrinare, è possibile scaricare il file di progetto allegato a questo tutorial che puoi compilare e confrontare.

Esegui l'applicazione. Vedrai il controller di navigazione e il pulsante Aggiungi. Premi il pulsante Aggiungi e otterrai una nuova ora nella tabella.

Ricapitolare

In questo tutorial abbiamo creato un'applicazione di esempio per archiviare dati semplici in un archivio di dati principali. L'applicazione ha eseguito la procedura di configurazione iniziale durante la creazione di un'applicazione con Core Data, definendo la struttura dati e recuperando i record dall'archivio dati.

Si spera che tu abbia avuto un'introduzione ai dati di base e puoi vedere quanto è facile da usare e come può migliorare le prestazioni e le funzionalità delle tue applicazioni.

Se vuoi saperne di più sui Core Data o vuoi dare un'occhiata dettagliata alla struttura del framework, la Documentazione degli sviluppatori Apple è il posto perfetto dove andare.

risorse

Documentazione degli sviluppatori Apple:

Introduzione alla programmazione dei dati core

Migrazione dei dati di base e controllo delle versioni

Guida alla programmazione di NSPredicate: