Stratégie avec NSURLConnection

FloFlo Membre
22:52 modifié dans API AppKit #1
Bonjour à  tous,

alors voilà , j'ai un model qui ressemble à  ça  A ->> B ->> C
où ->> représente une "relationship many to many"

Les B et les C on besoin d'être mis à  jour via des données téléchargées d'internet, je vois deux stratégies :

1) A utilise une NSURLConnection pour télécharger toutes les données (des B et des C) et ensuite parse les données et mets à  jour tout les B et les C.

2) Chaque B et chaque C possède une NSURLConnection, téléchargent leurs données chacun dans leur coin et se mette à  jour tout seul.

Quelle stratégie serait la plus profitable selon vous ?

Je me pose également une autre question, dans une logique MVC, est-ce vraiment aux objets du model de faire ce genre de chose ?

Merci d'avance pour vos réponses  :)

Réponses

  • CéroceCéroce Membre, Modérateur
    22:52 modifié #2
    dans 1248948408:

    Quelle stratégie serait la plus profitable selon vous ?


    C'est difficile de répondre sans savoir ce que sont B et C.
    Typiquement, si leurs instances sont très nombreuses, les NSURLConnection vont pomper beaucoup de ressources.
    Si elles sont peu nombreuses, en programmation objet il est souvent plus facile et plus évolutif que chaque objet gère ses propres affaires, donc utilise sa propre NSURLConnection.

    dans 1248948408:

    Je me pose également une autre question, dans une logique MVC, est-ce vraiment aux objets du model de faire ce genre de chose ?


    Oui, complètement.
  • FloFlo Membre
    22:52 modifié #3
    Merci pour cette réponse :o

    En effet, les C peuvent être assez nombreux et ç'est vrai que je n'y avait pas pensé mais du coup ça fait un thread par objet...

    Dommage parce que je trouvais également la stratégie 2 beaucoup plus "objet" et surtout beaucoup plus simple à  réaliser  :(
  • AliGatorAliGator Membre, Modérateur
    22:52 modifié #4
    Ca dépend, tu peux garder l'aspect conception objet et avoir un DownloadManager qui aurait par exemple une pile de requêtes à  réaliser, avec les demandeurs desdites requêtes, et se chargerait de gérer les connections donc d'avoir les NSURLConnections et par exemple de n'en gérer que 5 à  la fois

    Donc t'aurais une pile (NSMutableArray?) d'objets indiquant {requête+demandeur}, et ton DownloadManager en dépile jusqu'à  5, et dès qu'il y a un download qui se finit il envoie l'élément téléchargé au demandeur associé, puis dépile la suivante.

    Après si tu fais de ton DownloadManager un singleton (ou alors si tu le mets dans le AppDelegate par exemple enfin un truc comme ça) pour qu'il soit accessible par tous, bah c'est gagné :
    1) Tu limites le nombre de NSURLConnections simultanées
    2) Tu centralises tes downloads et ton module de récupération des données mises à  jour
    3) Tu restes objet tout de même.
  • FloFlo Membre
    22:52 modifié #5

    Ca dépend, tu peux garder l'aspect conception objet et avoir un DownloadManager qui aurait par exemple une pile de requêtes à  réaliser, avec les demandeurs desdites requêtes, et se chargerait de gérer les connections donc d'avoir les NSURLConnections et par exemple de n'en gérer que 5 à  la fois

    Donc t'aurais une pile (NSMutableArray?) d'objets indiquant {requête+demandeur}, et ton DownloadManager en dépile jusqu'à  5, et dès qu'il y a un download qui se finit il envoie l'élément téléchargé au demandeur associé, puis dépile la suivante.

    Après si tu fais de ton DownloadManager un singleton (ou alors si tu le mets dans le AppDelegate par exemple enfin un truc comme ça) pour qu'il soit accessible par tous, bah c'est gagné :
    1) Tu limites le nombre de NSURLConnections simultanées
    2) Tu centralises tes downloads et ton module de récupération des données mises à  jour
    3) Tu restes objet tout de même.


    YES ! ça dépote comme idée, j'aime bien !  :o

    J'y avais pas du tout pensé, merci !  :)
  • 22:52 modifié #6
    C'est exactement ce que j'ai fait pour télécharger des Images sauf qu'en plus je stocke ça sur l'iPhone par la suite plutôt que de saturer l'iPhone en mémoire de UIImage  :crackboom:-
    Si ça t'intéresse je peux te filer la classe, c'est un singleton aussi.
  • FloFlo Membre
    22:52 modifié #7

    Si ça t'intéresse je peux te filer la classe, c'est un singleton aussi.


    Tu parles que ça m'intéresse ?  :o

    J'veux bien, merci c'est super sympa, histoire de récupérer quelques (bonnes) idées !  :)
  • 22:52 modifié #8
    Voila,

    En gros le principe d'un singleton, si tu ne connais pas, c'est d'avoir une classe initialisée une seule fois dans ton programme, et utilisable par tout le monde. C'est un peu le principe de NSUserDefaults, t'as pas à  faire de retain, release et tout le tralala, et toute tes classes partagent le meme NSUserDefaults.

    De la même manière donc, tu appelles le singleton comme ceci (example pris à  partir de mon code) :
    <br />PicGetter* getter = [PicGetter sharedPics];<br />
    


    comme si tu faisais
    <br />NSUserDefaults* prefs = [NSUserDefaults standardUserDefaults];<br />
    


    J'ai repris le principe de l'App Store pour charger les images, c'est à  dire qu'à  chaque décélération d'un scroll sur ma UITableView, je charge les URLs associés aux "visibleCells".
    "picName", même si ça porte à  confusion, est en fait l'URL de l'image sous forme NSString.

    Après y'a deux autres méthodes qui retournent chacune un BOOL. Je pense qu'on aurait pu faire autremement mais bon, voici le fonctionnement :
    <br />- (BOOL)isLoadingPic:(NSString*)picName;<br />// Permet d&#39;informer la UITableView qu&#39;elle n&#39;a pas besoin de re-rappeler le chargement <br />// de l&#39;image via internet étant donné que le chargement est entrain d&#39;être effectué<br />
    


    <br />- (BOOL)isLoadingFinishedForPic:(NSString*)picName;<br />// Permet d&#39;informer la UITableView que le chargement a bien été effectué, et donc que <br />//l&#39;image existe (ou pas - dans ce cas, on retourne &quot;nil&quot; de toute manière).<br />
    


    Dans tout les cas, si l'un des deux retourne YES, mon controller ne rappelle pas le chargement des images pour les cells visibles. Il attend patiemment l'information du delegate :
    <br />- (void)picGetterDidGetImage:(PicGetter*)picGetter<br />
    


    Dès que mon controller est informé d'une "réussite", il appel immédiatement :
    <br />- (UIImage*)picForName:(NSString*)picName<br />// Retourne la UIImage chargée précédemment, et enregistrée dans l&#39;iPhone<br />
    



    Toutes ces informations ((NSString*)picName, (BOOL)isLoading, (BOOL)isLoaded) sont stockées dans un NSDictionary par image.
    Afin de vérifier facilement si une image est déjà  en chargement ou déjà  chargée, on stocke tous ces dictionnaires dans un "master dictionary" (j'aime bien ce terme :p ) qui aura comme clés les URLs (NSString) des images. Comme ça on peut simplement faire [picsList objectForKey:picName]!=nil par exemple pour vérifier l'existence de l'image demandée.


    J'espère que ça t'aidera un peu quand même.. Bien que je suppose que ce n'est pas vraiment ce que tu veux, au moins tu peux voir comment est foutu un singleton, comment charger des images de façon "intelligentes" histoire de ne pas trop bousiller la mémoire de l'iPhone, etc...
    Après, étant donné que les images sont appelées à  n'être chargées que lorsque qu'un scroll a pris fin, il est évident qu'on se retrouve avec pas plus de .. boarf on va dire 5-6 images à  charger d'un coup. ça rejoins un peu ce qu'expliquait Ali au niveau d'un download manager avec téléchargements simultanés limités.
  • juillet 2009 modifié #9
    Ah, j'oubliais. Pour faire un simple essai, tu peux par exemple appeler "loadPics:" en lui envoyant une NSArray qui contient des URLs (sous forme NSString, n'oublie pas) vers des images.

    Je t'ai fait un projet rapidos qui utilise ma classe "PicGetter" :
  • 22:52 modifié #10
    N'empêche je fais n'importe quoi  ::) On est dans la section Mac  ::)
    Bon ben oublie le projet d'exemple, mais le Singleton peut t'aider un brin.
    (Faut vraiment que je m'habitue au fait que la section Mac soit passée à  droite  >:) )
  • yoannyoann Membre
    22:52 modifié #11
    Jette un oe“il du coté des NSOperation et NSOperationQueue pour créer ton download manager, c'est fait pour ça !
  • FloFlo Membre
    22:52 modifié #12
    Merci Eaglelouk  :)

    Côté singleton ça va je suis rodé, j'avais même essayé de faire un schéma d'héritage entre eux (sujet sur ce forum) mais bon c'était pas très concluant !

    Merci pour ton exemple, je vois le principe du downloadManager !


    (Faut vraiment que je m'habitue au fait que la section Mac soit passée à  droite  )


    Moi aussi je trouve ça super déroutant, j'ai déjà  faillit poster dans la mauvaise section plusieurs fois !


    Jette un oe“il du coté des NSOperation et NSOperationQueue pour créer ton download manager, c'est fait pour ça !


    Ha oui tiens ! Bonne idée aussi, je vais essayer de faire un mix de tout ça, ça m'a l'air d'être des stratégies plutôt propres et efficaces !

    Merci à  tous en tous cas :)
  • 22:52 modifié #13
    Attention me semble que NSOperation n'est là  que depuis Leopard. Mais je doute que tu veuille faire un truc compatible Tiger  ;D
  • FloFlo Membre
    22:52 modifié #14

    Attention me semble que NSOperation n'est là  que depuis Leopard. Mais je doute que tu veuille faire un truc compatible Tiger 


    Oui en effet, je suis sur une appli "10.5 only"  <3
  • mpergandmpergand Membre
    22:52 modifié #15
    Et quelle est l'utilité du multi threads alors que NSURLConnection possède un mode asynchrone ?
    Même schlum, grand adepte du multi threads, semble enfin admettre que c'est inutile !

    http://forum.macbidouille.com/index.php?s=&showtopic=304559&view=findpost&p=3033706

    On peut éventuellement envisager d'effectuer tous les chargements dans un thread séparé, histoire de soulager la run loop du main thread, sous réserves que ça soit vraiment nécessaire ...
  • AliGatorAliGator Membre, Modérateur
    22:52 modifié #16
    Oui je suis un peu de l'avis de mpergand, j'ai pas proposé NSOperation et NSOperationQueue parce que leur but est de créer des opérations asynchrones (potentiellement interdépendantes) facilement... Mais là  NSURLConnection sait déjà  fonctionner en mode asynchrone. Donc je doute que NSOperation soit utile, et de plus il ne me semble pas qu'on puisse configurer la NSOperationQueue pour par exemple n'avoir QUE 5 threads / NSOperations à  la fois...

    Donc NSOperation et NSOperationQueue je suis aussi assez adepte quand le contexte s'y prête, mais là  je suis pas sûr... Le seul but c'est d'éviter d'avoir 100 NSURLConnections tournant toutes en même temps, mais d'en limiter à  par exemple 5 simultanées... Et là  pas vraiment besoin de NSOperations, si ?
  • mpergandmpergand Membre
    juillet 2009 modifié #17
    Et avoir 100 threads c'est contre productif, car le système passe son temps à  switcher d'un thread à  l'autre.

    J'ai fait un test de ce genre, et les performances s'effondrent au delà  de 50 threads (si je me souviens bien)

    Je pense qu'il faut tenir compte du nombre de cores physiques/logiques

    Et pour ce faire j'utilise cette méthode :
    <br />&nbsp; &nbsp; int val = 0;<br />&nbsp; &nbsp; size_t sval = sizeof(int);<br />	<br />&nbsp; &nbsp; if (sysctlbyname(&quot;hw.logicalcpu&quot;, &amp;val, &amp;sval, NULL, 0) == 0)<br />			MAX_THREAD=val;<br />	NSLog(@&quot;max thread %d&quot;,MAX_THREAD);
    




  • FloFlo Membre
    22:52 modifié #18
    Sinon, au-delà  de l'idée du download manager qui me semble bonne (peut-être pas avec NSOperation du coup  ;D) pour optimiser ya l'option suivante.

    Toujours avec le schéma suivant A ->> B ->> C  :

    1. A fait remonter les données des B et des C pour construire l'URL pour tout le monde. (main thread)

    2. A télécharge toutes les données d'un seul coup (une seule NSURLConnection en mode asynchrone ou A est le delegate)

    3. A parse les données dans un thread concurrent pour construire un NSDictionary avec.

    4. Les B et les C récupèrent chacun leur données respectives via le NSDictionary et se mettent à  jour (dans le main thread).

    Avantages :
    - le parsing des données se fait en parallèle et la mise à  jour finale des B et des C dans le main thread est beaucoup plus rapide.

    Inconvénients:
    - conso mémoire liée au NSDictionary intermédiaire ?
    - est-ce que le boulot confié à  A ne devrait du coup pas l'être plutôt à  un controller ?

    ça me semble pas mal comme ça aussi non ?  :)
  • FloFlo Membre
    22:52 modifié #19
    Ben quoi... j'ai dis une bêtise ?  ;)
  • schlumschlum Membre
    22:52 modifié #20
    dans 1249039544:

    Même schlum, grand adepte du multi threads, semble enfin admettre que c'est inutile !


    Moi, adepte du multi-thread ?  :)
    Au boulot, je suis sur un buffer manager qui fonctionne avec 9 threads et sans un seul mutex  ;D
    Une entrée, une sortie, une action d'entrée (décompression dans un format), une action de sortie (compression dans un autre format), un hash d'entrée, un hash de sortie, un hash intermédiaire, un thread de stats et un thread de smoothing  :o
Connectez-vous ou Inscrivez-vous pour répondre.