[Résolu] Chargement d'un XML volumineux : En background - Multi view

Am_MeAm_Me Membre
septembre 2014 modifié dans API UIKit #1

Hello.


 


Voilà  dans mon application :


Un utilisateur clique sur un bouton qui va initialiser un custom NSObject.


Pour l'initialiser je charge une page XML que je parse.


 


Il se trouve que cette page XML peut être très très grande : pour vous dire cela peut varier entre 100 lignes à  20000 oui 20 mille selon ce que l'utilisateur a sélectionné bref.


Jusque la tout va bien ça charge en moins de 15-20 (errata) c'est plutôt moins de 5 secondes (au max) et mes objets sont initialisés.


 


Alors quel est le problème vous allez me dire ?


J'ai un bouton sur ma vue permettant de dismiss la vue courante pour revenir à  ma vue principal. Bah si l'utilisateur coupe en plein milieu ça m'embête: 


du coup ce que je fais pour le moment : c'est dans la nouvelle vue : je regarde si mon objet a eu le temps de s'initialiser sinon je recommence la requête.


 


Du coup si l'utilisateur part encore dans une autre vue je refais le test. 


1) C'est lourd


2) C'est long parfois


 


Voilà  j'ai pleins de petites idées en tête pour optimisé la chose et éviter de faire n'importe quoi


 


1) le classique : bloquer les boutons : mettre un truc qui tourne pour dire à  l'utilisateur "attend" : perso je ne suis pas fan de cette solution


 


2) Faire une classe ou un truc en GLOBAL et ça jouerait un peu le rôle du background.


 


3) Vous êtes des pro je pense que vous avez de meilleurs solutions.


 


PS : Ah oui je fait mes requêtes avec NSURLSession



NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request
completionHandler:
^(NSData *data, NSURLResponse *response, NSError *error) {
}
[task resume]


MAJ : Pour parser j'utilise : NSXMLParser


 


MAJ 2 : j'avais vu ça il y a un bon bout de temps Test XMLParser Cocoa sur le site Raywenderlich il conseillais :


  • If you want to read extremely large XML documents, performance is the critical issue here. You'll want to consider libxml2 SAX, TBXML, or libxml DOM for this, depending on what your exact situation is.

 


Mais bon les noms n'ont pas l'air très enthousiaste et ça a du surement évoluer entre temps


Mots clés:
«13

Réponses

  • Bonjour,


    je n'ai pas compris si ton objectif est de continuer le chargement jusqu'au bout quand il a été initié pour le mettre en cache ou simplement d'interrompre quand l'utilisateur renonce à  visualiser la page ?

  • Am_MeAm_Me Membre
    septembre 2014 modifié #3

    Moi je veux que ça continu en background quoi : jusqu'au bout . Et je ne veut pas faire attendre l'utilisateur pour le chargement complet.


    Car en changeant de view ça coupe pour le moment.


     


    PS : D'ailleurs j'ai mis à  jour mon contenu la haut pour spécifier le parser que j'utilise.


  • Am_MeAm_Me Membre
    septembre 2014 modifié #4

    En fait j'ai peut-être exagéré sur les 20 secondes ça devait être mes NSLOg qui étaient retard : disons qu'il y'a une limite de vitesse aussi sur le terminal


     


    MAJ : finalement 5 secondes au maximum sur les 20000 lignes (j'ai enlevé les NSLOg qui ne servait à  rien j'ai laissé le dernier qui me dit : "J'ai fini" en gros)


  • AliGatorAliGator Membre, Modérateur
    Je ne suis pas sur de comprendre ton problème : si tu respectes bien le pattern MVC la requête et le parsing sont totalement décorrellés de l'aspect vue/écran donc je vois pas en quoi changer d'écran aurait un impact sur la requête et le parsing qui normalement sont fait dans des classes modèle / service dédiées et indépendantes des classes ViewController...


    Sauf si tu ne respectes pas le MVC et alors là  oui c'est logique que tu aies ce genre de souci mais bon la solution est simple : appliquer le pattern MVC :-P
  • pour tes NSLog, au lieux d'afficher le contenu de ton xml affiche le temps écoulé pour le chargement... En fait cela ne change pas grand chose au problème on peut toujours avoir des lenteurs réseau etc... tu devais alors peut-être avoir un objet spécialisé pour le chargement qui gère en même temps un cache. Cet objet serait une instance d'un objet toujours présent dans ton applie (AppDelegate par exemple) ou un singleton. L'url pourrait servir de clef pour ton cache (NSCache), et au début du chargement tu peux mettre en valeur un @(YES) ou autre pour que tu saches qu'il est en chargement. Ta page à  afficher pourrait observer cette valeur en KVO, de manière à  mettre ton affichage à  jours une fois les données arrivées. Ceci étant dit le KVO sur un cache c'est un peu glissant car si les données sont effacées ca risque de te poser des problèmes, mais tu peux utiliser un dictionnaire au lieu d'un NSCache.


  • AliGatorAliGator Membre, Modérateur
    NSURLCache ou NSCache, des completionBlock, le service classique quoi, comme on fait pour toutes les applis.


  • Je ne suis pas sur de comprendre ton problème : si tu respectes bien le pattern MVC la requête et le parsing sont totalement décorrellés de l'aspect vue/écran donc je vois pas en quoi changer d'écran aurait un impact sur la requête et le parsing qui normalement sont fait dans des classes modèle / service dédiées et indépendantes des classes ViewController...


    Sauf si tu ne respectes pas le MVC et alors là  oui c'est logique que tu aies ce genre de souci mais bon la solution est simple : appliquer le pattern MVC :-P




     


    Le MVC en java jai étudié


    Le MVC en général je comprend


    Le MVC sur cocoa  :o


     


     




    pour tes NSLog, au lieux d'afficher le contenu de ton xml affiche le temps écoulé pour le chargement... En fait cela ne change pas grand chose au problème on peut toujours avoir des lenteurs réseau etc... tu devais alors peut-être avoir un objet spécialisé pour le chargement qui gère en même temps un cache. Cet objet serait une instance d'un objet toujours présent dans ton applie (AppDelegate par exemple) ou un singleton. L'url pourrait servir de clef pour ton cache (NSCache), et au début du chargement tu peux mettre en valeur un @(YES) ou autre pour que tu saches qu'il est en chargement. Ta page à  afficher pourrait observer cette valeur en KVO, de manière à  mettre ton affichage à  jours une fois les données arrivées. Ceci étant dit le KVO sur un cache c'est un peu glissant car si les données sont effacées ca risque de te poser des problèmes, mais tu peux utiliser un dictionnaire au lieu d'un NSCache.




     


    Alors le fait d'utiliser un singleton me permettrai de respecter MVC ? Non je dois mal comprendre


     


    En gros faut que j'utilise un NSCache en singleton. Le NSCache lui se débrouille pour charger le XML. 


     


    En fait je vais être plus précis :


     


    Voici un dessin que je viens d'effectuer. 

  • AliGatorAliGator Membre, Modérateur

    Bah pourtant le MVC est un design pattern, c'est pas tant lié que ça à  un langage, même si la façon de l'implémenter est lié avec le framework qui propose plus ou moins de facilités.


     


    Mais le fondement même de Cocoa est le MVC. Tout est basé dessus dans les frameworks.


     


    Je t'invite, non, je t'oblige à  te documenter sur le MVC en Cocoa et comment le mettre en place. C'est même en général parmi les premières choses qu'on apprend quand on commence à  faire du Cocoa.


     


    Pour ton cas, ce n'est pas à  ton controlleur et encore moins à  ta vue de contenir le code qui a toute l'intelligence du parsing. Et même pour le download avec NSURLSession. Tu es plutôt sensé avoir une partie métier dans ton application qui gère ce genre de choses, qui doit être totalement indépendante de ton View/Controller.


    En effet le jour où tu veux porter ton application sur OSX, ou même sans parler de ça le jour où tu veux changer totalement l'ergonomie de ton application, y'a pas de raison que toute la partie métier, URLs à  requêter, parsing de la réponse de ces URLs, etc... change. Elle doit donc être dans des objets dédiés, indépendants des vues et des contrôlleurs. C'est la base même de MVC.


  • donc si je comprends tu ne veux pas que  tes données survivent à  ta vue de détails, (car il s'agit du résultat d'une recherche donc éphémère). Mais tu veux que cela n'entrave pas l'affichage de ta page de détails ?


    Si c'est le cas pas de mise en cache possible, si l'utilisateur ne patiente pas tu ne vas pas lui garder les données car il ne va pas les demander à  nouveau.


  • Am_MeAm_Me Membre
    septembre 2014 modifié #11


    Je t'invite, non, je t'oblige à  te documenter sur le MVC en Cocoa et comment le mettre en place. C'est même en général parmi les premières choses qu'on apprend quand on commence à  faire du Cocoa.




     


    Oui chef !


     


    Alors je comprend pas trop 



    Pour ton cas, ce n'est pas à  ton controlleur et encore moins à  ta vue de contenir le code qui a toute l'intelligence du parsing. Et même pour le download avec NSURLSession. Tu es plutôt sensé avoir une partie métier dans ton application qui gère ce genre de choses, qui doit être totalement indépendante de ton View/Controller.



     


    J'utilise un NSObject héritant implémentant les méthodes de NSXMLParserDelegate pour le parse. Tout ça c'est dans un fichier à  part.


    Ce que je fais par contre dans mon ViewController c'est vrai : je fait appel au parseur et NSURLSession à  l'intérieur 


    Du coup ce que tu es en train de m'expliquer c'est qu'il faut que je créer un objet qui lance les requêtes NSURLsession, AFNetworking ? (ou plusieurs objets d'ailleurs)


     


    ________________________________________


     


     


    Alors denis_13 les données XML chargé dans la fenêtre "Search" seront affiché dans la fenêtre "Detail"


    Comme tu le constate il y'a le "Home" entre donc ce que je disais c'est que jamais l'utilisateur ne mettrait moins de 5 secondes pour attérir la bas (car ma cellule est une sorte de tiroir : qui quand on clique dessus donne accès à  un des bouton dont UN qui permet d'accéder à  la vue détaillé)


     


    Je veux que mes données survivent entre le passage de la vue Search -> Home


  • denis_13denis_13 Membre
    septembre 2014 modifié #12

    si tu veux que tes données survivent en revenant sur home, il faut les stocker dans un object qui ne dépend pas de ta vue où tu les affiche (donc les mettre en cache), non ? autrement dit, le M de MVC doit survivre à  la manipe, tu ne vas pas le stocker dans ton contrôleur.


     


    PS: NSXMLParserDelegate est un protocole, on en hérite pas mais on l'implémente... (tu le sais puisque tu l'as utilisé, mais ta phrase doit choquer tout le monde ici... moi en premier).


  • Am_MeAm_Me Membre
    septembre 2014 modifié #13

    Oui U_U desolé c'est bien implémenter je me mêle les pinceaux en ce moment . Corrigé d'ailleurs


     


    Bah donc je crée un objet et cette objet aura plein de singletons à  ce que je comprend ?


    C'est un peu comme un objet qui contiendra toute les résultats de mes requêtes : ça m'a l'air pas mal comme idée


     


    Mais d'abord faut que comprenne comment on utilise/respecte le MVC sur cocoa.


  • colas_colas_ Membre
    septembre 2014 modifié #14

    Tu as fait un joli schéma avec tes vues. Je suis sûr que tu nous feras un joli dessin avec tes classes. 


     


    Tu auras plusieurs singletons, chacun gérant une tâche :


    - un singleton qui gère tes requêtes réseaux (éventuellement couplé à  un cache)


    - un singleton qui s'occupe de qq chose de très différent mais global à  ton app (par exemple : récupérer les données que tu stockes)


     


    Tout le monde pourra demander des choses à  tes singletons.


     


    Tes vues <-> tes view controllers <-> tes singletons. 


  • Am_MeAm_Me Membre
    septembre 2014 modifié #15

    Ah oui @AliGator


     


    Ce que je comprend dans le MVC cocoa (en même temps c'est pas difficile) c'est que le Modèle va me servir pour mes requêtes (pareil qu'en java) mais ma Vue et mon Controleur je bloque : sachant que dans mon storyboard j'ai une UIViewController et que j'ai posé les objets directement sur le storyboard : quel rôle joue la Vue dans l'histoire ? Faut-il créer un fichier juste pour elle. 


    Je ne comprend plus (en Java c'est simple un fichier Model.java, View.java et Controller.java) mais en cocoa  :o


  • pas plein de singleton, mais un singleton qui contient un cache, ou ajouter ce cache à  une instance de ton AppDelegate, c'est un peu comme le bout de l'oeuf il y a des avis divergents sur la question


  • ton contrôleur c'est ton UIViewController (ou plutôt la sous classe que tu en as dérivé), la vue c'est [self view]




  • Tu as fait un joli schéma avec tes vues. Je suis sûr que tu nous feras un joli dessin avec tes classes. 


     


    Tu auras plusieurs singletons, chacun gérant une tâche :


    - un singleton qui gère tes requêtes réseaux (éventuellement couplé à  un cache)


    - un singleton qui s'occupe de qq chose de très différent mais global à  ton app (par exemple : récupérer les données que tu stockes)


     


    Tout le monde pourra demander des choses à  tes singletons.


     


    Tes vues <-> tes view controllers <-> tes singletons. 




     


    AH d'accord okay. J'y vois plus clair. Ca à  l'air plus propre de faire ça comme ça d'ailleurs : hannn je vais mettre du temps pour changer le code : mais ça sera pour une bonne cause


     


    Donc je récapitule


     


    Mon objet aura plein de méthode et ça donnera genre


     


    [MonObjet sharedImage]


    [MonObjet sharedResultXML]


     


    etc ..

  • la vue est une sous classe de UIView


  • Am_MeAm_Me Membre
    septembre 2014 modifié #20


    ton contrôleur c'est ton UIViewController (ou plutôt la sous classe que tu en as dérivé), la vue c'est [self view]




     


    Bah donc ce que j'en tire de ce que tu viens de dire le MVC consiste à  créer un fichier Model.h et .m et c'est lui qui s'occupera d'appeler mon singleton. 


    Et la viewController gere deja la vue. J'ai n'ai pas besoin de faire quelque chose de ce côté .


  • ce serait plutôt [[MonModel sharedModel] image] et [[MonModel sharedModel] xmlResult]


  • Ah ok. Oui tu as raison. Mon singleton c'est mon sharedModel


     


    Après cela veut dire que mon sharedModel sera mis à  jour en continu.


    Par exemple au démarrage de l'appui je n'ai pas besoin du xmlResult mais c'est que quand j'accèderai à  ma SearchView que j'initialiserai son xmlResult


  • en fait dans ton contrôleur tu as des propriétés qui pointent sur les différents éléments de ton interface, que tu accède par code self monTitreLabel] setText:<span style="color:rgb(40,40,40);font-family:helvetica, arial, sans-serif;font-size:12px;">[[MonModel sharedModel] titre


  • denis_13denis_13 Membre
    septembre 2014 modifié #24

    oui, tu déclenches la recherche que si nécessaire, ensuite tu auras intérêt a faire observer ton modèle (KVO) par le contrôleur de ta vue de détails


  • Oui mon controller a des 



    @property (weak, nonatomic) IBOutlet UILabel *name;

    etc ... relié à  partir du storyboard à  leur destination.


     


    Et en temps normal entre la vue Home->Détail et une autre vue détail je partageais un NSObject (custom) à  partir du prepareForSegue.


    Du coup plus besoins de le partager dans le segue car je pourrais y accéder grâce au singleton c'est bien ça ?




  • oui, tu déclenches la recherche que si nécessaire, ensuite tu auras intérêt a faire observer ton modèle (KVO) par le contrôleur de ta vue de détails




    KVO ==> équivalent d'un observer ?


     


    Donc le contrôleur de ma vue doit observer mon modèle si j'ai bien compris. Ca parait logique. Mais est-ce réellement nécessaire ?


    Ce que je veux dire on peut faire avec ou sans ?

  • heu... en pratique ton viewController reçois des infos pour te dire quand la vue est chargée et autre info du genre (rotation par exemple) les propriétés que tu lui ajoutes te permettent d'accéder aux éléments d'interface. Pour le modèle tu te créés une classe qui va bien et que tu vas modifier et observer dans ton modèle  pour mettre ton interface ou ton modèle à  jour


  • A priori, oui plus besoin de segue. (perso je n'en ai jamais utilisé, ni de storyboards)


  • si pas de KVO, tu peux utiliser une notification pour informer ton contrôleur de l'arrivée des données du parse XML




  • heu... en pratique ton viewController reçois des infos pour te dire quand la vue est chargée et autre info du genre (rotation par exemple) les propriétés que tu lui ajoutes te permettent d'accéder aux éléments d'interface. Pour le modèle tu te créés une classe qui va bien et que tu vas modifier et observer dans ton modèle  pour mettre ton interface ou ton modèle à  jour




     


    tu voulais dire dans ta viewController ?


     


     




    A priori, oui plus besoin de segue. (perso je n'en ai jamais utilisé, ni de storyboards)




     


    Oui du coup il ne me serviront plus à  rien les segues.



  • Mais est-ce réellement nécessaire ?




     


    Oui tu peux faire sans. Mais est-ce que ce sera aussi rapide, simple, maintenable aisément ?


    Tu pourrais faire avec un callBack, mais ça veut dire que ton singleton doit connaà®tre les viewControllers. Du coup, le jour où tu devras changer du code dans tes viewControllers (genre tu en supprimes un ou pour ajouter deux), tu devras retoucher à  ton singleton.


     


    Dans ce cas, je ne vois pas le problèmes avec les notifications.

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