Core data : oui ou non ?

2»

Réponses

  • Le cas du lecteur RSS est l'exemple souvent évoqué par Daniel Pasco, le développeur de NetNewsWire, qui utilisait précédemment CoreData mais qui n'arrivait pas à  modifier tous les articles d'un coup de façon performante (Pour marquer tous les articles comme déjà  lus).


    D'après son anecdote, pour faire cette action, on est obligé de rapatrier tous les éléments et traiter chacun d'entre eux, puis sauvegarder. En SQL, il lui suffit d'une requête "update".


  • CéroceCéroce Membre, Modérateur

    Après j'ai regardé pas mal les lecteurs RSS sur iOS et la plus part font des mises à  jour indiquant qu'ils avaient supprimés Core Data donc j'ai pensé tout de suite à  la formation en me disant c'est surement à  cause des prefs.


    Je pense plutôt que ces applis cherchent à  faire de la synchro iCloud de bases Core Data.


    Le sujet a déjà  été évoqué, ça fonctionne très mal.

  • Excellent !! Passionnant toutes ses réponses !


     


    Le cas évoqué par Oliv me parle sur les problèmes sur la façon de mettre tous les articles lus ou non lus voir archivés. Après je ne me suis pas vraiment penché sur la question ... C'est ça le problème ;)


     


     


     


    Dès ce premier projet avec CoreData, j'ai utilisé le framework MagicalRecord qu'on m'a fortement conseillé, et effectivement qui simplifie énormément la vie (surtout que quand tu découvres CoreData pour la première fois et que tu vois les tutos qui te montrent qu'il faut écrire 30 lignes pour configurer ta stack CoreData, ça fait peur et décourage un peu, alors qu'avec MagicalRecord ça se fait en une seule ligne limite c'est magique voire déroutant de simplicité). 


     


    Ali, je vais regarder ce framework du coup !!


     


    K.

  • Excellent !! Passionnant toutes ses réponses !


     


    Le cas évoqué par Oliv me parle sur les problèmes sur la façon de mettre tous les articles lus ou non lus voir archivés. Après je ne me suis pas vraiment penché sur la question ... C'est ça le problème ;)


     


     


     


     


    Ali, je vais regarder ce framework du coup !!


     


    K.


     


    Faut pas hésiter à  utiliser les outils d'études de performances, tester surtout sur le iDevice le plus lent, de faire du traitement asynchrone et éventuellement créer des projets XCode en dehors de ton application qui te permettront de tester plusieurs types de solutions et stratégies.

  • Merci à  tous pour ces retours !


     


    Finalement... je me suis lancé dans CoreData !


    Pour l'instant mon appli qui avant marchait avec NSKeyedArchiver ne fonctionne plus... (pour des raisons de bindings qui pointent vers du nulle je pense), mais c'est en correction :)


     


    J'ai installé MOgenerator : un peu déroutant pour un novice d'ajouter une cible, de faire du script, d'avoir des erreurs de compatibilité avec ARC... mais j'y suis arrivé :) J'avoue avoir un peu hésiter avant de mettre mon code dans les classes qui hérite de NSManagedObject mais bon, j'y suis allé franco (pour mettre des valeurs initiales).


     


    Je vais aller faire un tour maintenant du côté de MagicalRecord.


     


    J'ai besoin de créer un "singleton core data" : il doit y avoir au plus une instance de mon objet dans mon fichier.


    À ce propos, j'ai un peu du mal à  comprendre à  quel moment sont chargées les données (plus précisément, à  quel moment le document est relié à  son fichier). C'est perturbant de voir que quand on ouvre un document, il continue d'utiliser init (au lieu d'un "initFromFile").


     


    Une des raisons qui me retenaient pour utiliser CoreData est que dans les anciennes versions, les relations étaient "non-ordonnées", alors que mon modèle à  besoin de A a pour attribut un tableau d'objets B. Comment faisait-on avant ? On le gérait à  la main (avec un attribut supplémentaire, genre : position) ? On utilisait les frameworks ad-hoc ?



  •  


    J'ai installé MOgenerator : un peu déroutant pour un novice d'ajouter une cible, de faire du script, d'avoir des erreurs de compatibilité avec ARC... mais j'y suis arrivé  :) J'avoue avoir un peu hésiter avant de mettre mon code dans les classes qui hérite de NSManagedObject mais bon, j'y suis allé franco (pour mettre des valeurs initiales).


     


     


    Attention, avec MOGenerator tu as une série de fichier machine et l'autre humain. Tu dois travailler dans les fichier humain et non machine si tu veux pas risquer de perdre tes données.


     


    J'ai besoin de créer un "singleton core data" : il doit y avoir au plus une instance de mon objet dans mon fichier.


    À ce propos, j'ai un peu du mal à  comprendre à  quel moment sont chargées les données (plus précisément, à  quel moment le document est relié à  son fichier). C'est perturbant de voir que quand on ouvre un document, il continue d'utiliser init (au lieu d'un "initFromFile").


     


    Une des raisons qui me retenaient pour utiliser CoreData est que dans les anciennes versions, les relations étaient "non-ordonnées", alors que mon modèle à  besoin de A a pour attribut un tableau d'objets B. Comment faisait-on avant ? On le gérait à  la main (avec un attribut supplémentaire, genre : position) ? On utilisait les frameworks ad-hoc ?


     

     



     


    Surtout pas de Singleton si tu es en mode document, sinon tu fais comment quand tu ouvre plusieurs documents ?


     


    Simplement c'est ta sous classe de NSDocument qui sera ton contrôleur CoreData et qui va rendre abstrait pour toi le multicontexte. Ensuite pour le chargement des données, va falloir ruser et lire correctement la doc sur la boucle de chargement de NSDocument car tu as énormément de choses faites d'origines avec.


     


    Pour les relations ordonnées avant, on passait par un champ position en effet, généralement un float pour pouvoir changer le classement sans se prendre la tête.

  • colas_colas_ Membre
    avril 2013 modifié #38

    Surtout pas de Singleton si tu es en mode document, sinon tu fais comment quand tu ouvre plusieurs documents ?


     


    Oui, je voulais dire une sorte de Singleton mais relatif à  chaque document. (pas à  l'application).


    Faut-il que je lise la doc de NSDocument ou celle de NSManagedDocument ?


    Les deux j'imagine !


     


    Les docs d'Apple sont longues je trouve.


     


    Merci !


  • NSManagedDocument ? On parle d'OS X ou de iOS là  ? NSManagedDocument ça n'existe pas il me semble, UIManagedDocument oui par contre, et c'est en gros ce qu'il faut ici, un [NS|UI]Document avec une stack CoreData.


  • CéroceCéroce Membre, Modérateur

    NSManagedDocument ? On parle d'OS X ou de iOS là  ? NSManagedDocument ça n'existe pas il me semble, UIManagedDocument oui par contre, et c'est en gros ce qu'il faut ici, un [NS|UI]Document avec une stack CoreData.


     


    Il existe un NSPersistentDocument sous OS X.

  • Il existe un NSPersistentDocument sous OS X.


     


    Au temps pour moi, je ne le connaissais pas !

  • Pour en revenir à  la question :


    Au départ, je me suis dit : CoreData ? à‰videmment, ça a l'air l'outil fait pour !


    Ensuite, après 3000 pages de doc', oui, elle est pas avare celle-là , je me suis demandé si c'était toujours une bonne idée...


    J'commence à  implémenter (j'me suis pas farci toutes ces pages de doc' pour rien quand même)...


    C'est assez long, chiant et complexe...


    Pour le moment, je suis dans la phase de compréhension de ce que je fais vraiment, même si ça reste assez encore confus dans mon esprit et que je pourrais largement améliorer des trucs, mais je ne désespère pas d'arriver à  la phase : " Core Data ? Oui, sans problème ".


     


    Là , j'ai vu parlé d'un truc Magic (MagicalRecord), faut croire que le nom du framework est vraiment fait pour, j'vais regarder, ça me titille, mais pas avant d'avoir finit d'en chier pour " maà®triser " CoreData seul...


  • Perso j'utilise CoreData avec MagicalRecord (j'ai commencé la doc de CoreData mais je l'ai pas fini). Mais je trouve que ça facilite beaucoup de chose. (la mise en place de CoreData, les requêtes etc...)  


  • AliGatorAliGator Membre, Modérateur
    avril 2013 modifié #44
    Pour donner un exemple en quoi MagicalRecord change tout dans l'approche qu'on peut faire de CoreData et simplifie drastiquement les choses :

     

    1) Pour créer sa "Core Data Stack" au lancement du projet, c'est à  dire tout initialiser, son contexte, son persitant store, etc.

     

    Sans MagicalRecord ça donne, d'après la doc Apple :

    NSManagedObjectContext *moc = nil;
     
        NSPersistentStoreCoordinator *coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: ...];
     
        NSString *STORE_TYPE = NSXMLStoreType;
        NSString *STORE_FILENAME = @CDCLI.cdcli;
     
        NSError *error;
        NSURL *url = [applicationLogDirectory() URLByAppendingPathComponent:STORE_FILENAME];
     
        NSPersistentStore *newStore = [coordinator addPersistentStoreWithType:STORE_TYPE
                                                   configuration:nil URL:url options:nil
                                                   error:&error];
     
        if (newStore == nil) {
            NSLog(@Store Configuration Failure\n%@", ([error localizedDescription] != nil) ? [error localizedDescription] : @Unknown Error);
        }
     
        moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
        [moc setPersistentStoreCoordinator:coordinator];

     

    C'est quand même pas rien, plusieurs lignes de code à  écrire, et qu'on aurait du mal à  deviner tout seul ! Et puis faut comprendre un peu ce que c'est que tout ces trucs, ce qu'est un NSPersistentStore et tout...

     

    Alors qu'avec MagicalRecord ? Une seule ligne suffit, pas besoin de se prendre la tête ni de savoir comment récupérer son ManagedObjectModel ou ce qu'est un PersistentStore, y'a juste à  écrire ça :
    [MagicalRecord setupCoreDataStack];
    Et voilà  c'est tout !

    Quand même 10x plus simple non ?

     

     

     

    Plus fort encore, dans mes Tests Unitaires, comme je veux pas que mes TU risquent de pulluer la base de mon application et que quand je lance mes tests je dois utiliser une base qui a toujours le même contenu pour toujours tester dans le même environnement... Bah avec MagicalRecord, pas de soucis je demande de créer ma CoreData Stack en mémoire, pour pas corrompre la base persistante sauvegardée dans un fichier que j'utilise pour le reste de l'appli :
    [MagicalRecord setupCoreDataStackWithInMemoryStore];
    Et voilà , en une ligne on a tout ce qu'il nous faut !
  • AliGatorAliGator Membre, Modérateur
    2) Autre exemple : si on imagine qu'on a une entité "Person" et qu'on veut la liste de toutes les personnes, triées par ordre alphabétique du nom de famille.

     

    Sans MagicalRecord, d'après la doc Apple, ça donnerait un truc comme ça :

    NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@Person inManagedObjectContext:moc];
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    [request setEntity:entityDescription];
     
    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@LastName ascending:YES];
    [request setSortDescriptors:@[sortDescriptor]];
     
    NSError *error;
    NSArray *array = [moc executeFetchRequest:request error:&error];
    Pfff que de lignes à  écrire pour un truc si basique ! Et de classes à  manipuler, entre NSEntityDescription, NSFetchRequest, etc...

     

    Avec MagicalRecord ? Pas besoin de se poser toutes ces questions ou de savoir ce que c'est que ce truc de NSEntityDescription ou quoi. J'ai une classe Person déjà  qui existe dans mon projet (créée par Xcode quand j'ai créé mon datamodel et que j'ai demandé de générer les fichiers .h/.m correspondants), donc pourquoi ne pas lui demander de me retourner toutes ses instances triées ? Allez hop, en une ligne c'est plié :
    Person findAllSortedBy:@LastName ascending:YES];
    Et voilà  on en parle plus !

     

     

     

    MagicalRecord, c'est que des trucs comme ça, tout plein de trucs qui te simplifient à  mort l'utilisation de CoreData en t'évitant de devoir te poser des questions comme "c'est quoi un NSPersistentStore" ou "putain faut vraiment tant de lignes de code pour faire une simple requête CoreData ?", du coup c'est du pur bonheur à  utiliser ;)
  • Ayant fait un mémoire sur la persistance des données, la question n'est pas un simple "utiliser Core Data ou non".


     


    Après tout dépend de la nature/complexité de ton projet. Pour moi, dès que tu as des relations entre tes objets que tu souhaites sauvegarder, la sauvegarde pure dans des fichiers est a exclure. Les points forts de Core Data (par rapport a SQLite par ex) sont multiples:


     


    - gestion de la mémoire en interne


    - support undo/redo


    - création graphique du modèle


    - migration


    - validation


    - support du "key-value observing"


    - interface simple (pas besoin de maitriser le langage SQL)


     


    ....

  • 2) Autre exemple : si on imagine qu'on a une entité "Person" et qu'on veut la liste de toutes les personnes, triées par ordre alphabétique du nom de famille.

     

    Sans MagicalRecord, d'après la doc Apple, ça donnerait un truc comme ça :



    NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@Person inManagedObjectContext:moc];
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    [request setEntity:entityDescription];
     
    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@LastName ascending:YES];
    [request setSortDescriptors:@[sortDescriptor]];
     
    NSError *error;
    NSArray *array = [moc executeFetchRequest:request error:&error];

    Pfff que de lignes à  écrire pour un truc si basique ! Et de classes à  manipuler, entre NSEntityDescription, NSFetchRequest, etc...

     

    Avec MagicalRecord ? Pas besoin de se poser toutes ces questions ou de savoir ce que c'est que ce truc de NSEntityDescription ou quoi. J'ai une classe Person déjà  qui existe dans mon projet (créée par Xcode quand j'ai créé mon datamodel et que j'ai demandé de générer les fichiers .h/.m correspondants), donc pourquoi ne pas lui demander de me retourner toutes ses instances triées ? Allez hop, en une ligne c'est plié :

    Person findAllSortedBy:@LastName ascending:YES];

    Et voilà  on en parle plus !

     

     

     

    MagicalRecord, c'est que des trucs comme ça, tout plein de trucs qui te simplifient à  mort l'utilisation de CoreData en t'évitant de devoir te poser des questions comme "c'est quoi un NSPersistentStore" ou "putain faut vraiment tant de lignes de code pour faire une simple requête CoreData ?", du coup c'est du pur bonheur à  utiliser ;)

     


    Autant pour les fetch je vois l'intérêt de MagicalRecord, autant ton exemple sur l'initialisation me fait un peu peur, dans la stack de code il y a des trucs importants quand tu fait du multicontexte et du thread...

  • 2) Autre exemple : si on imagine qu'on a une entité "Person" et qu'on veut la liste de toutes les personnes, triées par ordre alphabétique du nom de famille.

     

    Sans MagicalRecord, d'après la doc Apple, ça donnerait un truc comme ça :



    NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@Person inManagedObjectContext:moc];
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    [request setEntity:entityDescription];
     
    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@LastName ascending:YES];
    [request setSortDescriptors:@[sortDescriptor]];
     
    NSError *error;
    NSArray *array = [moc executeFetchRequest:request error:&error];

    Pfff que de lignes à  écrire pour un truc si basique ! Et de classes à  manipuler, entre NSEntityDescription, NSFetchRequest, etc...

     

    Avec MagicalRecord ? Pas besoin de se poser toutes ces questions ou de savoir ce que c'est que ce truc de NSEntityDescription ou quoi. J'ai une classe Person déjà  qui existe dans mon projet (créée par Xcode quand j'ai créé mon datamodel et que j'ai demandé de générer les fichiers .h/.m correspondants), donc pourquoi ne pas lui demander de me retourner toutes ses instances triées ? Allez hop, en une ligne c'est plié :

    Person findAllSortedBy:@LastName ascending:YES];

    Et voilà  on en parle plus !

     

     

     

    MagicalRecord, c'est que des trucs comme ça, tout plein de trucs qui te simplifient à  mort l'utilisation de CoreData en t'évitant de devoir te poser des questions comme "c'est quoi un NSPersistentStore" ou "putain faut vraiment tant de lignes de code pour faire une simple requête CoreData ?", du coup c'est du pur bonheur à  utiliser ;)

     


    J'ai déjà  entendu parlé ici de MagicalRecord. Ca à  l'air pas mal du tout (et j'ai confiance en ton avis et en celui qui a démarré ce projet :-) ). Ceci dit, en lisant les quelques exemples donnés, il me semble que pour des requêtes un peu complexes ou pour les optimisations, le nombre de lignes de code nécessaires est à  peu près identiques à  celui qu'on utiliserait pour du core data pur et dur.


     


    La seule différence notable que j'ai constatée dans un tel car est le masquage du context core data (et la gestion des erreurs) : l'exécution d'une requête est réalisée par l'intermédiaire d'une catégorie NSManagedObject et non d'un NSManagedObjectContext.


     


    Ce qui me fait penser que pour celui qui se met à  core data via MR, la première chose dont il n'a pas à  se soucier c'est la notion de context (sans doute plus compliqué à  comprendre que le NSFetchRequest).

  •  

    Plus fort encore, dans mes Tests Unitaires, comme je veux pas que mes TU risquent de pulluer la base de mon application et que quand je lance mes tests je dois utiliser une base qui a toujours le même contenu pour toujours tester dans le même environnement... Bah avec MagicalRecord, pas de soucis je demande de créer ma CoreData Stack en mémoire, pour pas corrompre la base persistante sauvegardée dans un fichier que j'utilise pour le reste de l'appli :

    [MagicalRecord setupCoreDataStackWithInMemoryStore];

    Et voilà , en une ligne on a tout ce qu'il nous faut !

     


    Pour mes tests j'utilise toujours des stores permanents (chargés automatiquement selon mon environnement d'exécution : test ou non). Cela me permet systématiquement de garder un oeil sur mes perfs.

  • AliGatorAliGator Membre, Modérateur

    Autant pour les fetch je vois l'intérêt de MagicalRecord, autant ton exemple sur l'initialisation me fait un peu peur, dans la stack de code il y a des trucs importants quand tu fait du multicontexte et du thread...


     


    Je te rassure j'ai cité ici évidemment les méthodes les plus simples et les plus couramment utilisées, mais il existe leurs variantes te permettant d'avoir plus de flexibilité.


    Dans le cas le plus courant, un appel à  setupCoreDataStack suffit. Si tu veux du plus fin, il y a d'autres méthodes te permettant de passer ton MOM si tu as plusieurs MOM, ou de maà®triser tes MOC utilisés. Et puis si tu veux vraiment, rien ne t'empêche d'initialiser ta stack CoreData à  la main sans utiliser les méthodes de commodité de MR. Si tu ne précises pas, il récupère le [NSManagedObjectModel mergedModelFromBundles:nil] donc un MOM qui contient l'agrégation de tous les MOM de ton projet, ce qui en général est suffisant et ce que tu veux car on a souvent qu'un seul MOM / .xcdatamodel dans notre projet. Mais si tu veux avoir des MOM séparés, gérer plusieurs MOM, plusieurs PersistantStore, et tout, y'a pas de soucis tu peux aussi.


     


    Après tout c'est comme pour des constructeurs de commodité de certaines classes Apple. Dans la plupart des cas elles conviennent et du coup ça simplifie ton code. Mais parfois si tu veux un contrôle plus fin, bah tu ne passes plus par les méthodes de commodité mais par les constructeurs de base. Bah c'est pareil


     


     


    Autre point de MR : l'utilisation d'un MOC par défaut par thread (contextForCurrentThread) de façon transparente dans la plupart des méthodes, mais avec la possibilité qd mm si tu veux de passer le MOC. Ca te permet du coup si tu as un cas d'usage classique pas trop complexe (ne nécessitant pas d'utiliser plein de MOC différents dans ton appli) de ne pas te prendre le chou, tu fais juste des trucs comme [Person createEntity] ou [Person findAll] et basta tu crées une entité Person ou récupères toutes les entités Person rapidement. Mais si tu veux faire ça dans un MOC particulier plutôt que le laisser le faire dans le contexte partagé, pas de soucis, tu fais un [Person createInContext:moc] ou un [Person findAllInContext:moc] et ça marche tout pareil !


     


    Donc si tu as besoin de préciser un MOC tu peux, mais si t'as pas besoin tu peux aussi t'en passer. Et MR gère les MOC nested, les MOC par thread, avec un dictionnaire gardant trace d'un MOC par thread pour utiliser directement le bon MOC par défaut pour le thread courant si tu ne précises pas toi-même le MOC...


     


    Bref, c'est plutôt bien pensé, te fournissant à  la fois des méthodes très simples d'un côté pour une utilisation rapide dans des cas classiques, et des méthodes plus complètes pour quand tu as besoin de plus de contrôle.

Connectez-vous ou Inscrivez-vous pour répondre.