Core Data est un framework fourni par Apple aux développeurs et décrit comme un "framework de gestion et de persistance des graphes d'objets basé sur un schéma". Qu'est-ce que cela signifie réellement? La structure gère l'emplacement de stockage des données, la manière dont elles sont stockées, la mise en cache des données et la gestion de la mémoire. Il a été porté sur l'iPhone à partir de Mac OS X avec la version 3.0 du SDK pour iPhone.
Dans ce tutoriel, je vais vous guider tout au long du processus de création d'un projet avec Core Data et vous montrer comment l'utiliser avec un simple UITableViewController. Ce sera une base de données simple, une table, qui stockera les temps intermédiaires et affichera les temps et leurs différences de division dans une vue UITableView..
Avant de commencer, il est important de comprendre pourquoi vous pouvez utiliser Core Data avec SQLite pour le stockage sur des listes de propriétés, un format XML personnalisé ou un accès direct à la base de données SQLite. L'API Core Data permet aux développeurs de créer et d'utiliser une base de données relationnelle, d'effectuer une validation des enregistrements et d'effectuer des requêtes à l'aide de conditions sans SQL. Cela vous permet essentiellement d’interagir avec SQLite dans Objective-C sans avoir à vous soucier des connexions ou de la gestion du schéma de base de données. La plupart de ces fonctionnalités sont familières aux personnes qui ont utilisé des technologies de mappage relationnel-objet (ORM) comme celles mises en œuvre dans Ruby. sur Rails, CakePHP, LINQ ou d’autres bibliothèques et frameworks qui permettent d’abstraire l’accès à la base de données. Le principal avantage de cette approche est qu’elle supprime le temps de développement nécessaire pour écrire des requêtes SQL complexes et gérer manuellement le SQL et la sortie de ces opérations..
L'exemple d'application que nous allons construire aujourd'hui est un simple chronomètre. Cela créera un nouvel enregistrement dans le magasin de données principal pour chaque tour que nous ferons. UITableView affichera ensuite la différence entre le tour actuel et le dernier tour..
Pour commencer, nous allons ouvrir XCode et créer un nouveau projet. Nommez-le comme vous voulez, je l'ai nommé "LapTimer". Nous allons créer une "application basée sur Windows" à partir de la sélection du modèle de nouveau projet. Assurez-vous que l'option "Utiliser les données de base pour le stockage" est cochée..
Le projet doit être familier avec ce que vous avez pu voir auparavant si vous avez déjà développé des applications iPhone..
Il n'y a pas beaucoup de configuration à faire car l'option "Utiliser les données de base pour le stockage" dans le modèle "Application basée sur une fenêtre" a automatiquement défini certaines variables importantes et créé des fichiers dans le projet pour nous..
Le fichier LapTimer.xcdatamodel est l'endroit où nous allons définir le schéma pour notre base de données SQLite. L'infrastructure Core Data a également été ajoutée au projet afin d'inclure les fichiers pour l'accès à l'API. Les autres modifications sont effectuées dans les fichiers d'application par défaut. Notamment, les fichiers de délégué d'application ont les méthodes pour configurer le magasin de données principal dans notre application et le référencer dans un enfant UIViewControllers..
Ce qui nous intéresse actuellement, c’est le fichier LapTimer.xcdatamodel sous "Ressources". Ce fichier vous donne une carte visuelle de votre schéma montrant des entités et des attributs..
Il existe différents termes et expressions utilisés dans les données de base qui peuvent être vaguement liés aux noms de bases de données classiques, mais ils ne sont pas identiques..
Une "entité", également appelée "objet géré", est similaire à une table. C'est la définition d'un objet qui contiendra une collection de données. Un objet entité contient des "attributs". Ceux-ci peuvent être vaguement associés à des colonnes, mais ces attributs ne se limitent pas au stockage de données. Les attributs peuvent définir une relation entre deux entités, une propriété extraite dynamiquement ou une propriété pour le stockage de données..
Dans le diagramme ci-dessus, vous pouvez avoir une idée de la flexibilité des objets dérivés des entités Core Data. Pour cet exemple d'application, nous aurons besoin d'une entité très simple. Nous appellerons l'entité "Event" qui stockera les enregistrements de nos tours.
Pour créer cette entité, nous allons cliquer sur le bouton [+] dans la première colonne (de gauche). Cela créera une nouvelle entité dans la liste et visuellement sur la carte en dessous des colonnes..
C'est un bel éditeur visuel pour votre modèle. Core Data fait vraiment le gros travail quand il s’agit de la partie "M" (modèle) de MVC. L'éditeur visuel affiche les relations, les propriétés et les entités du magasin, tandis que le schéma est créé et géré dynamiquement pour vous. Ceci est similaire à Interface Builder, car il s'occupe de l'allocation, de la gestion et du placement d'objets pour vous sur une vue UIV, sans une seule ligne de code..
Avec la nouvelle entité Event en place, nous voulons créer une "propriété". Puisque cette propriété stockera des données, nous allons la définir comme un "attribut". Donc, ce nouvel attribut va simplement stocker la date actuelle pour la création de l’enregistrement. Dans notre exemple d'application, nous utiliserons cela pour référencer nos temps au tour..
La colonne suivante à droite indique où nous définissons nos propriétés (assurez-vous que l'entité Event est sélectionnée). Donc, créez une nouvelle propriété en utilisant le bouton [+] dans la colonne, sélectionnez "Ajouter un attribut".
Nous appellerons l'attribut "timeStamp" et définirons son type sur "Date". La liste déroulante de sélection de type contient des types de données de colonne similaires à ceux disponibles dans les systèmes de bases de données relationnelles tels que PostgreSQL ou MySQL, y compris des types de données tels que des entiers, des flottants, des chaînes, des booléens, des dates et des données binaires (blobs)..
Pour cet attribut, nous n'avons besoin d'aucune autre option sélectionnée ou modifiée.
C'est tout pour le fichier xcdatamodel, et nous pouvons l'intégrer dans notre application. C'est le bon moment pour sauvegarder votre travail.
Si vous avez utilisé une structure MVC avec des définitions de modèle de base de données définissant la structure ou les comportements d'une table, il s'agira d'une tâche familière..
Nous devons commencer par créer une définition de l'entité. Nous faisons cela en créant une classe NSManagedObject de l’entité et en définissant les variables du magasin..
Ceci est un processus simple. Sélectionnez l'entité Event dans le fichier LapTimer.xcdatamodel et choisissez Fichier> Nouveau fichier. Vous verrez qu'il y a un nouveau modèle de fichier dans la section "Cocoa Touch Class" appelé "Managed Object Class"..
Sélectionnez le modèle et cliquez sur "Suivant". L'écran suivant ne fait que définir l'emplacement où vous sauvegardez le fichier et la cible avec laquelle l'inclure. Tout est correct par défaut. Cliquez à nouveau sur "Suivant". L'écran suivant est l'endroit où vous définissez les entités pour lesquelles vous souhaitez créer des classes NSManagedObject. S'il n'est pas sélectionné, sélectionnez Evénement et assurez-vous que les cases "Générer des accesseurs" et "Générer des propriétés Obj-C 2.0" sont cochées, nous n'aurons pas besoin de validations pour l'instant. Hit Terminer.
Alors maintenant, nous avons 2 nouveaux fichiers dans notre application. Event.m et Event.h. Ils définissent la classe NSManagedObject pour l'entité Event créée dans notre magasin de données principal. Ils définissent le champ timeStamp. Ainsi, lorsque nous voulons utiliser la classe Event, nous pouvons accéder à l'attribut..
Événement.h
#importation@interface Event: NSManagedObject @property (nonatomic, keep) NSDate * timeStamp; @fin
Event.m
#import "Event.h" @implementation Event @dynamic timeStamp; @fin
Comme pour les définitions de modèle dans d'autres frameworks et langages, vous pouvez ajouter des méthodes personnalisées pour tous les enregistrements de l'entité Event. Vous remarquerez que l'attribut timeStamp a été ajouté et attribué en tant qu'objet NSDate..
Avec la configuration de Core Data, il est maintenant temps de travailler sur une partie de la logique du contrôleur dans l'application. Nous utiliserons UITableViewController comme interface principale de l'application pour afficher les temps au tour et enregistrer une nouvelle heure..
Nous allons donc créer le nouveau UITableViewController avec Fichier> Nouveau fichier. Ensuite, dans la section iPhone, sélectionnez "UIViewController subclass" et cochez "UITableViewController subclass", mais ne cochez pas les cases associées à l'utilisation de XIB ou au ciblage d'un iPad. Nous n'utiliserons pas de XIB pour ce contrôleur. Appelez le nouveau fichier "TimeTableController" et terminez l'assistant de fichier..
Dans ce contrôleur, nous aurons besoin de 2 propriétés, une référence à NSManagedObjectContext et un tableau pour stocker les enregistrements de UITableView. En plus de définir ces propriétés, nous allons importer le fichier Event.h afin de pouvoir utiliser la classe.
TimeTableController.h
#importation#import "Event.h" @interface TimeTableController: UITableViewController NSManagedObjectContext * managedObjectContext; NSMutableArray * eventArray; @property (nonatomic, keep) NSManagedObjectContext * managedObjectContext; @property (nonatomic, keep) NSMutableArray * eventArray; - (void) fetchRecords; - (void) addTime: (id) expéditeur; @fin
Qu'est-ce qu'un NSManagedObjectContext? Il est appelé «bloc-notes» pour Core Data dans l'application pour gérer la récupération, la mise à jour et la création d'enregistrements dans le magasin. Il gère également quelques fonctionnalités fondamentales dans Core Data, notamment les validations et la gestion annulation / restauration pour les enregistrements..
Le contexte de l'objet géré est la connexion entre votre code et le magasin de données. Toutes les opérations que vous allez exécuter pour Core Data le font par rapport au contexte de l'objet géré. Lorsqu'une demande est exécutée, le contexte de l'objet géré communiquera avec le coordinateur du magasin persistant, chargé de mapper les objets sur les données du magasin de données. Cela permet aux données de base d'être flexibles entre différents formats de magasin de données. Voici un diagramme de la façon dont cela ressemble.
Avec le fichier d'en-tête défini, nous devons propager les propriétés et méthodes allouées dans le fichier d'implémentation..
TimeTableController.m
#import "TimeTableController.h" @implementation TimeTableController @synthesize managedObjectContext, eventArray; //… //… code commenté par défaut du modèle de fichier //… - (void) viewDidLoad [super viewDidLoad]; self.title = @ "Lap Times"; UIBarButtonItem * addButton = [[[UIBarButtonItem alloc]] initWithBarButtonSystemItem: UIBarButtonSystemItemAdd cible: action autonome: @selector (addTime :)); self.navigationItem.rightBarButtonItem = addButton; [version addButton]; [auto fetchRecords]; - (void) addTime: (id) expéditeur Event * event = (Event *) [NSEntityDescription insertNewObjectForEntityForName: @ "Event" inManagedObjectContext: managedObjectContext]; [événement setTimeStamp: [date du NSDate]]; NSError * error; if (! [managedObjectContext save: & error]) // Ceci est une erreur grave disant que l'enregistrement // n'a pas pu être enregistré. Conseillez à l'utilisateur de réessayer ou de redémarrer l'application. [eventArray insertObject: event atIndex: 0]; [self.tableView reloadData]; - (void) fetchRecords // Définit notre table / entité pour qu'elle utilise NSEntityDescription * entity = [NSEntityDescription entityForName: @ "Event" inManagedObjectContext: managedObjectContext]; // Configuration de la demande d'extraction NSFetchRequest * request = [[NSFetchRequest alloc] init]; [request setEntity: entité]; // Définit le mode de tri des enregistrements NSSortDescriptor * sortDescriptor = [[NSSortDescriptor alloc] initWithKey: @ "timeStamp" ascending: NO]; NSArray * sortDescriptors = [NSArray arrayWithObject: sortDescriptor]; [request setSortDescriptors: sortDescriptors]; [version de sortDescriptor]; // Récupère les enregistrements et gère une erreur NSError * error; NSMutableArray * mutableFetchResults = [[managedObjectContext executeFetchRequest: erreur de demande: & error] mutableCopy]; if (! mutableFetchResults) // Gère l'erreur. // Ceci est une erreur grave et devrait conseiller à l'utilisateur de redémarrer l'application // Sauvegarder nos données récupérées dans un tableau [self setEventArray: mutableFetchResults]; [version mutableFetchResults]; [demande de libération]; //… //… plus de commentaires sur les modèles et de définitions de méthodes par défaut //… - (void) dealloc [managedObjectContext release]; [version de eventArray]; [super dealloc]; @fin
C’est une bonne partie du code, donc examinons chaque méthode individuellement. Il y a du code à partir de quand vous créez le fichier à partir du modèle, des méthodes commentées comme viewDidUnload, que je viens d'omettre de ce qui précède.
Nous commençons avec l'appel habituel à la super classe. Ensuite, nous définissons le titre de UINavigationBar. Après cela, nous devons définir le bouton que nous utiliserons pour ajouter un enregistrement au magasin de données principal. Lorsque le bouton est enfoncé, nous lui demandons d'appeler le sélecteur addTime: il interagira ensuite avec Core Data. Après les versions requises, nous appelons la fonction fetchRecords qui est expliquée ci-dessous..
Ceci est déclenché lorsque l'utilisateur appuie sur UIBarButtonItem dans le coin supérieur droit de UINavigationBar. Nous devons créer un nouvel enregistrement d'événement avec le NSDate actuel et l'enregistrer dans la base de données..
Nous créons un nouvel enregistrement d'événement appelé NSEntityDescription. Ceci est votre ligne dans la base de données pour le nouvel enregistrement. Pour ce faire, nous définissons le nom d'entité auquel appartient l'enregistrement et fournissons le NSManagedObjectContext. La date actuelle est ensuite définie par rapport à l'attribut timeStamp.
L'opération de sauvegarde est ensuite effectuée, mais une disposition permet de traiter une erreur en cas d'échec de l'insertion. Vous lancerez généralement un UIAlertView indiquant que l'enregistrement n'a pas été créé et demandez éventuellement à l'utilisateur de réessayer ou de fermer et rouvrir l'application..
L'enregistrement doit être ajouté au tableau à partir duquel UITableView se nourrit. Ensuite, il faut demander à UITableView de recharger les données. Vous pouvez le faire de manière plus graphique avec une animation, mais dans l’intérêt du tutoriel, restons simples..
Cette méthode récupérera les données du magasin et les ajoutera au tableau que nous avons dans le contrôleur. Pour plus de détails sur l'obtention des enregistrements, examinons NSFetchRequest..
Core Data utilise une approche différente pour extraire les données de sa base de données. C'est un magasin de données NoSQL, ce qui signifie que toutes les conditions d'une requête sont basées sur des méthodes. C’est formidable, car le magasin de base, SQLite, pourrait être remplacé par une autre technologie de base de données et la seule chose à modifier serait la connexion et les pilotes de la base de données..
Donc, pour créer une demande, nous créons un objet NSFetchRequest. C'est l'objet de base sur lequel les conditions de la requête seront définies. Nous pouvons définir des conditions pour faire correspondre une propriété particulière en fonction de la manière dont les enregistrements sont classés. Vous pouvez en savoir plus sur les données de base et ses conditions pour NSFetchRequests dans leur documentation..
La création d'une nouvelle requête NSFetch est simple. Il vous suffit de définir l'entité dont vous voulez les enregistrements et le NSManagedObjectContext.
NSEntityDescription * entity = [NSEntityDescription entityForName: @ "Event" inManagedObjectContext: managedObjectContext]; NSFetchRequest * request = [[NSFetchRequest alloc] init]; [request setEntity: entité];
L'entité est définie à l'aide d'un objet NSEntityDescription qui nécessite le nom de l'entité et le NSManagedObjectContext. La demande d'extraction est ensuite créée en passant la description de l'entité. Cela équivaudrait à la première partie d'une instruction SQL:
SELECT * FROM 'events'
Dans notre exemple d'application, nous trions les données en fonction du timeStamp de manière décroissante. Pour ce faire, nous utilisons un descripteur NSSort.
NSSortDescriptor * sortDescriptor = [[NSSortDescriptor alloc] initWithKey: @ "timeStamp" ascending: NO]; NSArray * sortDescriptors = [NSArray arrayWithObject: sortDescriptor]; [request setSortDescriptors: sortDescriptors]; [version de sortDescriptor];
Le descripteur NSSort est créé et nous définissons l'attribut que nous souhaitons trier et qu'il soit croissant, dans le cas présent, nous voulons qu'il descende et qu'il soit défini sur NO. La demande d'extraction peut prendre plusieurs descripteurs de tri afin qu'elle accepte un tableau lors de la définition des descripteurs de tri. Comme nous n'en voulons qu'un, nous avons juste besoin de créer un tableau avec un objet. Nous plaçons le tableau de descripteurs par rapport à la requête d'extraction et c'est ce.
Pour définir une condition correspondant à un contenu d'enregistrements, la classe NSPredicate entre en jeu. Cela permet à la requête d'extraction de correspondre ou de définir une plage que le contenu d'un enregistrement doit respecter. C'est l'équivalent de vos équivalents, supérieurs et inférieurs aux correspondances en SQL. Il a plus que vos fonctions SQL de base, que vous pouvez voir ici.
Définir un prédicat peut être très simple.
NSPredicate * predicate = [NSPredicate predicateWithFormat: @ "(nom comme% @) ET (anniversaire>% @)", lastNameSearchString, birthdaySearchDate];
Utilisation de NSPredicate predicateWithFormat: est une méthode simple et familière qui vous permet de définir les conditions de la requête. Pour une explication détaillée sur NSPredicates, la documentation Apple contient de très bons guides..
Lorsque vous avez défini les conditions dans votre demande d'extraction, vous pouvez ensuite l'exécuter..
NSMutableArray * mutableFetchResults = [[managedObjectContext executeFetchRequest: erreur de demande: & error] mutableCopy];
Cela retournera un tableau d'objets d'entité, NSManagedObjects, à utiliser dans votre sortie de données.
Avec les données extraites de Core Data et stockées dans eventArray, nous pouvons maintenant sortir ces enregistrements dans UITableView.
La première chose à faire est de dire à la table que nous n’aurons besoin que d’une section et du nombre de lignes à utiliser..
Extrait de TimeTableController.m
-(NSInteger) numberOfSectionsInTableView: (UITableView *) tableView return 1; - (NSInteger) tableView: (UITableView *) tableView numberOfRowsInSection: (NSInteger) section return [eventArray count];
Si vous avez déjà utilisé un UITableViewController, la fonction suivante devrait être simple..
-(UITableViewCell *) tableView: (UITableView *) tableView cellForRowAtIndexPath: (NSIndexPath *) indexPath statique 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: [ligne de chemin d'index]]; Evénement * 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 = [[event timeStamp] timeIntervalSinceDate: [previousEvent timeStamp]]; [cell.detailTextLabel setText: [NSString stringWithFormat: @ "+%. 02f sec", timeDifference]]; else [[cell.detailTextLabel setText: @ "---"]; cellule de retour;
La cellule affichera 2 valeurs, en utilisant le style UITableViewCellStyleValue1. La gauche sera le temps du tour et la droite sera la différence en secondes par rapport au précédent record.
Étant donné que cette méthode itère, nous devons faire très attention à la charge, car elle peut poser le périphérique si elle n'est pas gérée correctement. Pour cette raison, NSDatFormatter est stocké sous forme de variable statique afin de pouvoir être réutilisé à chaque itération sans l'allouer ni le relâcher à chaque fois..
Le chargement différé est une technique dans laquelle vous retardez autant que possible la demande ou l'attribution d'une propriété. Cela aide à garder la mémoire basse et lors des fonctions itératives, ceci est primordial. Il est essentiel de savoir quand et comment allouer des données pour maintenir une application mobile rapide. La désallocation de l’objet est aussi importante, le plus tôt sera le mieux..
cellForRowAtIndexPath: est une méthode itérative et toutes les données traitées ou allouées dans cette méthode doivent en particulier être gardées au minimum. Cette méthode est exécutée chaque fois qu'une cellule entre en vue. Ainsi, lorsqu'un utilisateur fait défiler rapidement cette méthode particulière, en fonction de la taille du jeu d'enregistrements, peut être appelée très fréquemment l'une après l'autre..
La tâche suivante consiste à obtenir l'objet événement associé à la ligne de la table à restituer. Comme nous devons obtenir l'enregistrement précédent pour la comparaison d'heures, il suffit de vérifier s'il existe un enregistrement précédent et de le stocker dans previousEvent. Si l'événement previousEvent existe, nous calculons le partage à l'aide de [NSDate timeIntervalSinceDate: (NSDate)]. Le textLabel et detailedTextLabel sont ensuite définis avec les valeurs que nous avons calculées.
Avec la configuration UITableViewController et la source de données de table associée au magasin de données central, il suffit de charger le contrôleur au démarrage de l'application..
Dans le contrôleur d'application, une propriété UINavigationController doit être définie. Ensuite, la méthode applicationDidFinishLaunching doit juste allouer le contrôleur et nous avons terminé.
LapTimerAppDelegate.h
@interface LapTimerAppDelegate: NSObjectNSManagedObjectModel * managedObjectModel; NSManagedObjectContext * managedObjectContext; NSPersistentStoreCoordinator * persistentStoreCoordinator; UIWindow * fenêtre; UINavigationController * navigationController; @property (nonatomic, rétention, lecture seule) NSManagedObjectModel * managedObjectModel; @property (nonatomic, rétention, lecture seule) NSManagedObjectContext * managedObjectContext; @property (nonatomic, rétention, lecture seule) NSPersistentStoreCoordinator * persistentStoreCoordinator; @property (nonatomic, keep) IBOutlet UIWindow * fenêtre; @property (nonatomic, keep) UINavigationController * navigationController; - (NSString *) applicationDocumentsDirectory; @fin
Extrait de LapTimerAppDelegate.m
#import "LapTimerAppDelegate.h" #import "TimeTableController.h" @implementation LapTimerAppDelegate @synthesize fenêtre, navigationController; - (void) applicationDidFinishLaunching: (UIApplication *) application TimeTableController * tableController = [[TimeTableController alloc]] initWithStyle: UITableViewStylePlain]; tableController.managedObjectContext = [self managedObjectContext]; self.navigationController = [[UINavigationController alloc] initWithRootViewController: tableController]; [version de tableController]; [window addSubview: [vue de self.navigationController]]; [fenêtre makeKeyAndVisible]; //… //… autres méthodes de template //… - (void) dealloc [managedObjectContext release]; [version managedObjectModel]; [version persistante de StoreStorecoordinator]; [libération de la fenêtre]; [version de navigationController]; [super dealloc];
Le fichier TimeTableController.h est inclus dans le délégué de l'application, puis alloué avec un UINavigationController..
Cela devrait être ça. Générez l'application pour vérifier les erreurs. Certains exemples de code ne sont que des extraits, aucun code généré lors de la création d'un fichier n'a été supprimé, mais simplement rempli. Si vous rencontrez des erreurs impossibles à résoudre, vous pouvez télécharger le fichier de projet joint à ce didacticiel. que vous pouvez ensuite compiler et comparer.
Lancer l'application. Vous verrez le contrôleur de navigation et le bouton Ajouter. Appuyez sur le bouton Ajouter et vous obtiendrez une nouvelle heure dans le tableau..
Dans ce didacticiel, nous avons créé un exemple d’application pour stocker des données simples dans un magasin de données principal. L'application a exécuté la procédure de configuration initiale lors de la création d'une application avec Core Data, de la définition de la structure de données et de l'extraction des enregistrements dans le magasin de données..
J'espère que vous avez une introduction à Core Data et que vous pouvez voir à quel point il est facile à utiliser et comment il peut améliorer les performances et les fonctionnalités de vos applications..
Si vous souhaitez en savoir plus sur Core Data ou obtenir un aperçu détaillé de la structure de la structure, consultez la documentation destinée aux développeurs Apple..
Documentation développeur Apple:
Introduction à la programmation de données de base
Migration de données de base et gestion des versions
Guide de programmation NSPredicate: