sendSynchronousRequest & time out
klozen
Membre
Bonjour à tous.
Je post se sujet car j'ai réussi à résoudre un problème /rolleyes.gif' class='bbc_emoticon' alt='::)' /> mais je ne sais pas comment ni pourquoi ... /crazy.gif' class='bbc_emoticon' alt=' ' />
Explication :
Je fait des requêtes http pour aller chercher un script php pour récupérer des données dans une base de données (MySQL avec charset par défaut donc Latin1). donc je créer ma requête (NSString) puis la passe en paramètre à une url exécutant un script php.
J'instancie donc un objet NSURL avec une string concaténé le plus classiquement du monde en passant par un stringByAddingPercentEscapeUsingEncoding:NSUTF8StirngEncoding.
Une fois le NSURL défini j'initialise un NSURLRequest de cette façon :
Je récupère ensuite le résultat :
Jusqu'a ce matin tout à toujours bien fonctionné le plus "fluidement" du monde cependant lors d'une compile de vérification ce matin (sans aucun changement de code) j'ai une erreur durant l'exécution de mon application.
Donc suite à des tests (NSLog, bp, ...) j'ai isoler le problème et il se trouve que c'est l'initialisation de _responseData qui plante. Enfin, plantage est un grand mot, je dirais plutôt que le senSynchronous fait des sienne avec un code error à 1001 représentant ni plus ni moins qu'un bon vieux time out.
Du coup j'ai tester en dur avec plusieurs requêtes différente (en dur) et il s'est avérer que le problème est totalement aléatoire (du moins c'est ma conclusion). J'ai vérifier les requêtes exécutées et qui avaient déjà posé problème à l'aide de cette même url via un navigateur et en directe sur la base de donnée et aucune erreur. Donc mes requêtes sont bonnes également.
J'ai essayer avec NSURLRequest alloc]initWithURL:_myUrl (on s'est jamais) mais toujours le même problème.
Du coup je déconnecte l'ipad, efface l'application de ce dernier, reboot du mac en quittant xCode correctement etc ... (tentative du désespoir /rolleyes.gif' class='bbc_emoticon' alt='::)' /> ) Je rebranche, je relance et recompile et toujours le même problème et toujours sans avoir modifier le code car il fonctionnais à 23h08 hier (dernière compile et test de ma part) alors pourquoi pas ce matin ?! /crazy.gif' class='bbc_emoticon' alt=' ' />
Du coup ca commence à m'agacer légèrement, j'ai cherché sur le web des syntaxe peut-être différente pour exécuter une requête, en vain. Je désinstalle à nouveau l'appli, je build, je débranche l'ipad, je lance l'application et bing plus aucun problème ... /implore.gif' class='bbc_emoticon' alt=' ' />
Je rebranche l'ipad pour recompiler avec xCode et suivre le déroulement de l'exécution du code et en effet je n'est plus de time out au niveau du sendSynchronous !
Alors ma question est simple, avez vous une idée de la ou le problème peut provenir ?
Je post se sujet car j'ai réussi à résoudre un problème /rolleyes.gif' class='bbc_emoticon' alt='::)' /> mais je ne sais pas comment ni pourquoi ... /crazy.gif' class='bbc_emoticon' alt=' ' />
Explication :
Je fait des requêtes http pour aller chercher un script php pour récupérer des données dans une base de données (MySQL avec charset par défaut donc Latin1). donc je créer ma requête (NSString) puis la passe en paramètre à une url exécutant un script php.
J'instancie donc un objet NSURL avec une string concaténé le plus classiquement du monde en passant par un stringByAddingPercentEscapeUsingEncoding:NSUTF8StirngEncoding.
Une fois le NSURL défini j'initialise un NSURLRequest de cette façon :
NSURLRequest *_urlRequest = [[NSURLRequest alloc]initWithURL:_myUrl cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:15.0];<br />
Je récupère ensuite le résultat :
NSData *_responseData = [NSURLConnection sendSynchronousRequest:_urlRequest returningResponse:&_reponse error&_error];<br />
Oui je sais le synchronous c'est pas terrible mais en l'occurrence je n'est pas besoin de faire de l'asynchrone ici.
Jusqu'a ce matin tout à toujours bien fonctionné le plus "fluidement" du monde cependant lors d'une compile de vérification ce matin (sans aucun changement de code) j'ai une erreur durant l'exécution de mon application.
Donc suite à des tests (NSLog, bp, ...) j'ai isoler le problème et il se trouve que c'est l'initialisation de _responseData qui plante. Enfin, plantage est un grand mot, je dirais plutôt que le senSynchronous fait des sienne avec un code error à 1001 représentant ni plus ni moins qu'un bon vieux time out.
Du coup j'ai tester en dur avec plusieurs requêtes différente (en dur) et il s'est avérer que le problème est totalement aléatoire (du moins c'est ma conclusion). J'ai vérifier les requêtes exécutées et qui avaient déjà posé problème à l'aide de cette même url via un navigateur et en directe sur la base de donnée et aucune erreur. Donc mes requêtes sont bonnes également.
J'ai essayer avec NSURLRequest alloc]initWithURL:_myUrl (on s'est jamais) mais toujours le même problème.
Du coup je déconnecte l'ipad, efface l'application de ce dernier, reboot du mac en quittant xCode correctement etc ... (tentative du désespoir /rolleyes.gif' class='bbc_emoticon' alt='::)' /> ) Je rebranche, je relance et recompile et toujours le même problème et toujours sans avoir modifier le code car il fonctionnais à 23h08 hier (dernière compile et test de ma part) alors pourquoi pas ce matin ?! /crazy.gif' class='bbc_emoticon' alt=' ' />
Du coup ca commence à m'agacer légèrement, j'ai cherché sur le web des syntaxe peut-être différente pour exécuter une requête, en vain. Je désinstalle à nouveau l'appli, je build, je débranche l'ipad, je lance l'application et bing plus aucun problème ... /implore.gif' class='bbc_emoticon' alt=' ' />
Je rebranche l'ipad pour recompiler avec xCode et suivre le déroulement de l'exécution du code et en effet je n'est plus de time out au niveau du sendSynchronous !
Alors ma question est simple, avez vous une idée de la ou le problème peut provenir ?
Mots clés:
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
Utilise plutôt sendAsynchronousRequest, c'est pas plus compliqué à utiliser et ça t'évitera des déboires de blocage de thread
Donc au final ça change pas grand chose à ton code déjà écrit, t'as juste à changer le nom de la méthode et mettre le code qui était après directement dans le block, et le tour est joué, et ça sera mieux et moins bloquant (et donc potentiellement évitera les timeouts en utilisant une queue dédiée pour la requête plutôt que de risquer qu'elle soit bloquée par le thread courant par deadlock...
... sauf que j'exécute ca dans une classe qui normalement me retourne un NSdictionnary suite à l'exécution de la requête passé en paramètre. Du coup vu que c'est asynchrome, le return s'effectue avant la requête et du coup me retourne un dictionnaire vide et donc n'affiche rien dans mes conteneur et ce malgré que quelques temps après la requête s'exécute bien avec le bon résultat ...
Du coup est ce que je doit faire un wait (ou équivalent en objective c) ou j'ai meilleur temps de réorganiser ma classe SQL à ce type d'utilisation ?
Ou alors avec un NSNotification qui m'indique que les datas sont remonté et que je peut les afficher pour ensuite effectuer un UIRefreshControle dans ma vue ?
Quand le serveur a répondu à la requête, le bloc passé à sendAsynchronousRequest: est exécuté. Si c'est ton contrôleur qui a appelé cette méthode, alors il n'a qu'à retirer la UIView bloquante et se rafraà®chir.
Il y a au moins trois manière d'implémenter un mécanisme de call-back:
- utiliser le centre de notification
Souvent une mauvaise idée parce que les notifications sont globales à l'application.
- utiliser le design pattern Délégation
C'est beaucoup mieux (dépendances limitées à un objet uniquement), mais un peu lourd à mettre en oe“uvre.
- utiliser un bloc
Suffit quand les callbacks sont simples, rapide à mettre en oe“uvre.
Pour finir, créer le corps des requêtes HTTP, gérer les erreurs et gérer l'asynchronisme est complexe; justement, AFNetworking gère tout ça. Si nous le plébiscitons ici régulièrement, c'est qu'il y a de bonnes raisons !
Il suffit de mettre le code où je t'ai dit de le mettre dans mon post plus haut, regarde de nouveau mon exemple de code avec commentaires, en particulier le commentaire "Ton code pour traiter _responseData, _response et _error" (qui correspond donc à ton code qui va construire ton NSDictionary à partir de la réponse puis en faire je ne sais quoi) !
Je n'ai pas mis ces commentaires pour rien !
Le problème étant qu'avec le sendSynchronous je fonctionnais avec une classe SqlPerso avec une méthode du type consultationDataServer qui est une fonction retournant un NSDictionnary.
Du coup dans une vue, dans son viewDidLoad, j'instancie mon objet SqlPerso et j'appel la méthode consultationDataServer avec en paramètre la requête préconstruite comme suit :
Ainsi je récupérais directement un Dictionnaire me permettant ensuite de l'afficher dans un tableView par exemple.
Sauf qu'avec l'asynchrome le dictionnaire sera forcément vide puisque le code est effectué dans un thread autre que le main. Le code dans le block asynchrome sera exécuté un peu plus tard. Sauf que c'est trop tard puisque le dictionnaire est déjà initialisé avec la ligne de code cité ci dessus et que par conséquent ma tableView traite un dictionnaire vide.
Dans cette méthode refresh (propre à la vue que l'utilisateur veux afficher) je place :
Cela semble fonctionné mais est ce que cette manière de procédé est correcte ?
PS : tu peux utiliser des classes toutes faites comme SVProgressHUD pour faire ta vue transparente d'attente, tu la trouveras sur GitHub et elle est plutôt bien faite et jolie, perso c'est celle que j'utilise tout le temps !
Du coup ça,modifie un peu le principe de fonctionnement mais étant donné que j'ai copié le fonctionnement du AFNetworking fournit sur gitub je pense que c'est bon /smile.png' class='bbc_emoticon' alt=':)' />
Si des personnes sont curieuse de se savoir exactement comment ça fonctionne je peut fournir ce que j'ai tester. J'integre ça dans mon projet lundi.
Merci pour vos réponse /wink.png' class='bbc_emoticon' alt=';)' />
3 secondes pour le download et 1 minute pour le mette en place /implore.gif' class='bbc_emoticon' alt=' ' />
finalement je vais l'utiliser par défaut maintenant /thumbsup.gif' class='bbc_emoticon' alt='' />
Actuellement je le lance avant le déclenchement du block puis le dismiss à la fin du dernier block d'exécution en asynchrone (avec AFNetworking) du coup cela crée le problème expliquer ce dessus.
Intégration d'un timer qui au bout de tant de temps d'exécution d'une NSOperationQueue fille (autre que mainQueue) affiche le message ou alors c'est à moi de gérer et en fonction du code exécuter je choisis ou non d'afficher le SVProgressHUD ?
J'ai vu qu'il était possible d'ajouter un afterDelay avant le dismiss mais j'aimerais aller plus loin en autorisant ou non l'affichage en fonction du temps d'exécution que le code va prendre. Par exemple au delà d'une demi seconde on affiche le message.
Très pratique pour tester ton appli avec des réponses toutes faites qui marchent même si la plateforme à qui tu dois envoyer des requêtes a un problème ou n'est pas prête (cas très courant du client qui te dit qu'il va te fournir un WebService mais seulement dans 2 mois...) et justement aussi et surtout pour simuler les cas un peu plus réalistes que tes tests en Wifi depuis ton canapés, genre quand tu as un réseau lent façon Edge ou 3D ou des pertes de connexion /wink.png' class='bbc_emoticon' alt=';)' />
Par contre j'ai remarqué quelque chose. Lorsque j'utilise mon application la ou je développe pas de problème de connexion ou d'exécution de requête via http/php etc.
Je prend mon iPad et je vais me connecter à un autre point wifi donc autre adresse ip publique, etc. Pas de problème la aussi tout fonctionne.
Seulement lorsque je reviens à l'endroit ou je développe donc avec ma connexion internet initial ça ne fonctionne plus. Je me retrouve avec des time out pour presque toutes mes requêtes !
Pourtant j'initialise mon NSURLRequest uniquement avec un NSURL (donc avec utilisation du cache) du coup j'ai essayé en utilisant IngnoringCacheData en gardant un timeoutInterval à 60 mais rien n'y fait ...
une petite idée de la ou cela pourrait venir ?
NB : autre observation, si je laisse la requête se mettre en time out au bout de 60s et que je la relance directement après elle s'exécute en mois d'une demi seconde ...
NB² : bien sur le time out est aléatoire c'est à dire pas tout le temps et pas toujours pour les même requêtes ...
Voici la structure du code pour l'exécution d'une requête :
Si tu n'utilises pas ta connexion data pendant un certain temps, il me semble que l'iPhone met "en veille" la puce qui gère la connection Data/Wifi, et du coup que quand tu fais une requête alors qu'elle était en veille, parfois elle met un peu de temps à se réveiller. Ce qui pourrait expliquer ce comportement, et dans ce cas même si tu faisais n'importe quelle requête avec Safari tu aurais le même problème, ce qui montrerait que ça n'est pas lié à ton application.
Je me suis également dit que ca pouvait venir des requêtes, j'ai testé une par une chacune de mes requêtes sur la bdd puis via mon script php dans safari et aucun bug.
Du coup j'ai tester tout au long de la journée et pour finir (et pour "limiter la casse" pour le moment) j'ai défini un time out interval de 3 avec une condition qui fait que si ma requête est en time out je relance ma requête en boucle (boucle infinie si ma requête reste en time out /baby.gif' class='bbc_emoticon' alt=' ' /> mais c'est uniquement pour les tests)
Par conséquent j'ai observer qu'en moyenne il va y avoir 2 time out dans une fourchette allant de 0 à 5 et ce jamais sur les mêmes requêtes (ça serais trop simple sinon /grin.gif' class='bbc_emoticon' alt=';D' /> ).
Je sais que mon code n'est pas très optimisé du fait que c'est ma toute première application mais c'est bizarre non ? J'utilise mal AFNetworking et les blocks ? Problème de cache local ou de cache tout court ? Etant donnée qu'avant tout mon code était basé sur une application avec des requêtes synchrone (voir début du sujet) j'ai du transiter sur AFNetworking, est ce que du coup ça provoquerais des dysfonctionnement au niveau de l'exécution du code ? Car la gestion de la récupération des données entre sendSynchronous et sendAsynchronous ou encore avec AFNetworking n'est pas du tout la même.