(Réglé) Traitement d'une requête très long (presque 20 secondes)
Ben77650
Membre
Bonjour à tous,
Je viens vers vous car j'ai un souci, j'ai une requête qui met presque 20 secondes a s'exécuter (alors qu'elle est toute simple), et soyons clair n'importe quel utilisateur n'attendra pas 20 secondes...
A titre de comparaison une autre requête me prends 8 sec (et mon boss trouve ça déjà très long)
Ci joint mon code:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.title=@Mon compte;
}
loadingView = [[UIView alloc] initWithFrame:CGRectMake(75, 200, 170, 170)];
loadingView.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.5];
loadingView.clipsToBounds = YES;
loadingView.layer.cornerRadius = 10.0;
actview = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
actview.frame = CGRectMake(65, 20, actview.bounds.size.width, actview.bounds.size.height);
[loadingView addSubview:actview];
loadingLabel = [[UILabel alloc] initWithFrame:CGRectMake(20, 80, 140, 22)];
loadingLabel.backgroundColor = [UIColor clearColor];
loadingLabel.textColor = [UIColor whiteColor];
loadingLabel.adjustsFontSizeToFitWidth = YES;
loadingLabel.text = @Récupération de vos données. Veuillez patientez svp...;
loadingLabel.lineBreakMode = NSLineBreakByWordWrapping;
loadingLabel.numberOfLines=0;
loadingLabel.textAlignment = NSTextAlignmentCenter;
[loadingLabel sizeToFit];
[loadingView addSubview:loadingLabel];
[self.view addSubview:loadingView];
[actview startAnimating];
[self getMemberInfo];
return self;
}
-(void)getMemberInfo
{
dispatch_queue_t downloadQueue = dispatch_queue_create("Get Member Infos", NULL);
dispatch_async(downloadQueue, ^{
NSData *result = [self searchMemberInfo];
id strResult=nil;
int cpt;
NSError* error;
strResult = [NSJSONSerialization JSONObjectWithData:result options:0 error:&error];
_liste = [[NSMutableArray alloc]init];
for(cpt=0; cpt<[strResult count];cpt++)
{
NSDictionary *dicoTemp = [strResult objectAtIndex:cpt];
NSMutableArray* arrayTemp = [[NSMutableArray alloc]init];
[arrayTemp addObject:[dicoTemp objectForKey:@myCivilitu]]; //0
[arrayTemp addObject:[dicoTemp objectForKey:@mySurname]]; //1
[arrayTemp addObject:[dicoTemp objectForKey:@myFirstName]]; //2
[arrayTemp addObject:[dicoTemp objectForKey:@myAdress]]; //3
[arrayTemp addObject:[dicoTemp objectForKey:@myZip]]; //4
[arrayTemp addObject:[dicoTemp objectForKey:@myCity]]; //5
[arrayTemp addObject:[dicoTemp objectForKey:@myTelephone]]; //6
[arrayTemp addObject:[dicoTemp objectForKey:@myEmail]]; //7
}
if(_liste.count!=0)
{
[actview stopAnimating];
[loadingView removeFromSuperview];
_civilite.text=[[_liste objectAtIndex:0]objectAtIndex:0];
_nom.text=[[_liste objectAtIndex:0]objectAtIndex:1];
_prenom.text=[[_liste objectAtIndex:0]objectAtIndex:2];
_region.text=@"";
_dep.text=@"";
_adresse.text=[[_liste objectAtIndex:0]objectAtIndex:3];
_cp.text=[[_liste objectAtIndex:0]objectAtIndex:4];
_ville.text=[[_liste objectAtIndex:0]objectAtIndex:5];
_tel.text=[[_liste objectAtIndex:0]objectAtIndex:6];
_mail.text=email;
}
});
}
- (NSData *)searchMemberInfo {
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@%@", @http://www.mywebsite.org/getInfos.php]];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
NSString *requestFields = @"";
requestFields = [requestFields stringByAppendingFormat:@pwd=%@&", password];
requestFields = [requestFields stringByAppendingFormat:@admail=%@", email];
requestFields = [requestFields stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSData *requestData = [requestFields dataUsingEncoding:NSUTF8StringEncoding];
request.HTTPBody = requestData;
request.HTTPMethod = @POST;
NSHTTPURLResponse *response = nil;
NSError *error = nil;
NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
if (error == nil && response.statusCode == 200) {
}
else {
}
return responseData;
}
NSLog(@liste: %@", _liste);
2014-05-20 10:31:59.422 MyApp[4294:4b13] liste: (
(
Mme,
Renault,
"M\U00e9gane",
"Place de Clichy",
75013,
"Paris, France",
0836656565,
"toto@toto.com",
)
)
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
Y'a un outil dans Intruments pour voir qu'est-ce qui prend exactement du temps si je me souviens bien.
Cela pourrait t'aider à voir qu'est-ce qui prends exactement du temps. Dès lors, on pourra penser à peut-être optimiser...
Oui, il faudrait savoir si c'est le serveur ou la base sur le serveur...
Si tu as des logs côté serveur...
D'abord merci de chercher à m'aider
Je ne pense pas que ce soit le serveur, car de ce que me dit mon boss, c'est bien plus rapide sur l'application Android qui se connecte au même serveur (concernant la partie ou moi je met 8 sec, sur Android il met 0.5 sec à tout récupérer...)
Et je ne trouve déjà pas "Instruments" alors je parle même pas de l'outil
Mon dieu, "sendSynchronousRequest"... faut pas chercher plus loin.
Oui je sais tu as mis tout ça dans une dispatch_queue, mais ça n'empêche, les NSURLConnection sont gérées par la RunLoop et non pas par un WorkingThread, donc faut utiliser ce qui est fait pour pour les gérer de façon asynchrone...
Créer une queue tout ça pour appeler du code synchrone, alors qu'il y a déjà des méthodes asynchrones pour ça est une très mauvaise idée. D'autant que la méthode de base est la méthode asynchrone, et que la méthode synchrone appelle la méthode asynchrone avec des semaphores pour bloquer l'exécution, donc avec ta dispatch_queue tu essayes de... rendre asynchrone un truc qui rend synchrone un truc qui est asynchrone... ou comment se compliquer la vie.
Enlève-moi ce sendSynchronousRequest et cette downloadQueue et fais les choses bien et comme conseillées dans le URL Loading Programming Guide et le Concurrency Programming Guide, en utilisant les méthodes asynchrones existantes pour faire de l'asynchrone, à savoir sendAsynchronousRequest.
Je suis quasiment sûr qu'avec ça tes temps de réponse vont drastiquement être réduits.
Ali merci pour cette réponse.
Mon prof d'iOS de cette année (un ancien étudiant, possédant son entreprise) nous avais dit d'utiliser cette méthode sendSynchronousRequest dans une Queue (je viens de vérifier sur son PDF la). Encore heureux que tu me dise cela, je n'utilise quasiment que cela.
Ne connaissant pas ces guide, peut tu me dire les bonnes manières de faire s'il te plait ?
Oui les dispatch_queue sont la méthode à utiliser pour faire des tâches en arrière plan en général (GCD c'est le pied)... sauf pour les tâches comme NSURLConnection qui se basent sur la RunLoop et ont surtout déjà une API pour faire de l'asynchrone, qui prend en compte la manière un peu spécifique dont fonctionne NSURLConnection.
Sinon concernant les guides, il suffit de chercher leurs noms sur Google.
D'accord merci et tiens voila le pdf de mon prof, si tu as d'autres améliorations / rectifications / conseils je suis preneur.
Merci
Entre autres, en plus de l'hérésie dont j'ai parlé au dessus concernant sendSynchronousRequest (à banir sans se poser de question, en aucun cas utiliser cette méthode, je me demande même pourquoi Apple la garde encore), je vois des trucs sur dispatch_get_current_queue, alors qu'il est à éviter.
D'ailleurs, dispatch_get_current_queue n'existait (à l'époque) que pour du debug, et à ne pas utiliser en production (c'est clairement indiqué dans sa doc). Et de toute façon maintenant il a été totalement supprimé de l'API (vu que Apple a réalisé que bcp l'utilisaient à tort).
D'ailleurs le fait d'avoir à savoir quelle est la dispatch_queue courante et souvent un signe qu'on n'a pas forcément compris la subtile différence entre les dispatch_queue et les threads (une dispatch_queue peut dispatcher les blocks qu'on lui soumet sur différents threads, une queue ne pilote pas forcément qu'un thread).
Normalement tu n'as pas de raison d'avoir à connaà®tre quelle est la dispatch_queue courante, la seule chose utile à connaà®tre éventuellement c'est si [NSThread isMainThread] pour savoir si tu as besoin de dispatch_(a)sync(dispatch_get_main_queue(), ^{...}) ton code UI-related ou si tu es déjà sur le MainThread et que tu peux l'exécuter direct.
Merci pour ces conseils en tout cas Ali, je prends bien note
On est bien d'accord que la bonne manière de faire est celle la ?
Je ne comprends pas pourquoi "request" vaut " http://www.monsite.org/getInfos.php" et non "http://www.monsite.org/getInfos.php?pwd=password&admail=email"
Quoique pour le paramètre "queue" de "sendAsynchronousRequest:queue:completionHandler:" tu pourrais mettre [NSOperationQueue mainQueue] car ici la NSOperationQueue qui est passée à ce paramètre (comme expliqué dans la doc) c'est la queue sur laquelle le code de ton completionHandler va être exécuté, pas la queue sur laquelle ta requête réseau va être exécutée, donc c'est pas forcément la peine d'avoir une NSOperationQueue d'arrière-plan pour ce que tu fais dedans, mais bon du moment que tu ne fais pas d'UI dans ton completionHandler ça va encore.
Pourquoi tu voudrais que l'URL de ta NSRequest ait ce contenu ? C'est pas ce que ton code demande.
Ton code demande d'encoder "pwd=password&admail=email" dans le HTTPBody (ce qui est logique si ta requête est du POST), donc ce "pwd=password&admail=email" est présent dans le corps de la requête POST, pas dans l'URL. C'est pas comme si tu faisais du GET, là tu utilises le HTTPBody pour passer tes paramètres donc rien de plus normal que ton URL, elle, ne change contienne pas ces paramètres car ce n'est pas ce que tu as demandé.
Bah en fait ça me chagrine, car mes labels restent avec la valeur par défaut, donc je me suis dit que l'erreur venait peut être de la mais visiblement c'est pas le cas.
Je continue de chercher, si jamais vous voyez faites moi signe.
Ps: J'ai juste modifié et changé en "[self searchMemberInfo];" dans initWithNibName
Edit: Chose étonnante ma liste est tout le temps vide, bizarre quand j'utilisais la manière synchrone avec une manière identique ça fonctionnait
Edit 2: Bon visiblement il ne rentre jamais dans la boucle for
Edit 3: ma variable strResult est vide, WTF ?
Edit 4: Si dans strResult, je remplace "data" par "requestData" ça m'affiche null
Edit 5: Data m'affiche <5b5d>
Data n'est pas vide (cf edit 5), en revanche requestData l'est lui
J'avais prévu le coup et déjà fait les logs suivants
Autrement dit, ton serveur te renvoie une réponse "200 OK", avec un JSON tout à fait valide. Il se trouve juste que ce JSON qu'il te renvoie correspond à un tableau totalement vide ("[]").
Pourtant mon JSON est pas vide vu qu'il marchais de manière synchrone et que j'y ai pas touché depuis.
Pareil je ne pense pas qu'il y ai d'erreurs dans mon code, j'ai copié collé celui du synchrone qui fonctionnait
à ce qui est posté dans les messages au dessus (Profil Controller)
Edit: Non je n'utilise pas GIT, je suis le seul développeur. En revanche chaque semaine j'enregistre l'état de l'appli, en faisant une sauvegarde
J'utilise GIT sur plein de projets sur lesquels je suis tout seul, et heureusement, car ça m'est extrêmement utile. Justement par exemple pour des cas comme celui que tu as, où on peut facilement avec GIT et ses outils basculer d'une version du code à l'autre, faire des diff, des bisect, des comparaisons, revenir à une version le temps de tester puis re-récupérer nos modifs après, faire un blame pour savoir à quel moment / quelle version tu as modifié telle ligne de ton code... bref tout ce que GIT apporte.
Utiliser GIT n'a pas forcément à voir avec le travail en équipe, il est tout aussi utile pour du travail en solo. Tu serais sous GIT tu aurais un moyen très simple de comparer tes 2 codes, rebasculer sur l'un ou l'autre et rajouter des logs sur chacune des 2 versions pour comparer les résultats au runtime.
Alors que faire une sauvegarde de ton projet, c'est toujours bien plus long et contraignant car tu dois garder tout plein de sauvegardes, tu n'as pas de date/message de commit/browser d'historique de code, ça prend du temps et surtout de l'espace (si ton projet fait 5Mo, ça veut dire que tu as 100Mo dès que tu as 20 sauvegardes de ton code ?! Alors que GIT ne garde que les diff et du coup le repo montera plutôt à 8Mo qu'à 100Mo...) et en plus de ça du coup vu le temps que ça prend de faire une copie/sauvegarde à chaque fois, tu en fais beaucoup moins souvent que tu ne ferais un commit GIT (perso je préfère faire plein de micro-commits que d'attendre la fin de la journée de travail pour faire un gros commit qui contient plein (trop) de choses, ça permet de découper plus finement les évolutions et de rendre la bisection bien plus efficace en cas de recherche de bug)
---
PS : Pas top la façon dont tu construits tes requestFields, au passage :
1) Allouer des NSString à chaque fois en demandant une nouvelle NSString avec "stringByAppendingFormat:" est un peu du gâchis, tu alloues une nouvelle chaà®ne à chaque fois c'est un peu du gâchis. Si tu comptes construire une chaà®ne bout par bout, utilise plutôt NSMutableString, et travaille directement sur cette NSMutableString plutôt que recréer des nouvelles instances à chaque fois.
2) Tu appliques "stringByAddingPercentEscapesUsingEncoding:" sur toute ta chaà®ne, alors qu'il ne faut pas remplacer tes "=" et "&" de toute ta chaà®ne, il ne faut échapper que les valeurs. Ainsi si tu veux passer 2 paramètres a et b donc les valeurs sont "val&a" et "je dis b=1", il faut échapper le "&" de "val&a" et le "=" de "je dis b=1" mais pas le "&" ni le "=" de "a=val%26a&b=je%20dis%20b%3D1".
En bref, dans ton cas il ne faut échapper que les variables nom_table, limite et rang, pas toute la chaà®ne
3) [NSString stringWithFormat:@%@", str] est inutile et du gâchis (je suis étonné que tu n'aies pas un warning, d'ailleurs). Pas la peine de passer par un format pour mettre une chaà®ne qui est directement un litéral. Autant directement utiliser la chaà®ne litérale str et enlever complètement ton stringWithFormat qui ne sert strictement à rien.
L'argument du travail en solo je l'ai cité, puisque c'est ce que m'a dit le collègue quand je lui ai demandé si lui utilisais Git.
Je t'avoue que Git je ne connais quasiment pas (même si je vais devoir m'y mettre pour les projets scolaires à rendre très prochainement).
Peut être du coup as tu un tutoriel simple (et si possible en français) sur comment utiliser Git ? Ca me serait très utile pour moi et mes camarades.
J'ai précisé que les requêtes sont différentes, ce sont 2 controllers bien distincts qui pointent sur 2 pages différentes, mais vu que le principe est quasi similaire (je l'ai repris et adapté) j'ai copié collé celui la.
J'ai bien pris note des 3 points que tu as évoqué
D'ailleurs quand tu crées un nouveau projet Xcode, il te propose de créer un repo local GIT par défaut.
Il y a plein de threads sur CocoaCafé où on parle déjà de GIT et où on explique plein de trucs à son sujet, donne des instructions pour démarrer avec GIT et tout, je te laisse faire une recherche.
D'ailleurs, il suffit de refaire la requête avec "curl" en ligne de commande, ou avec "PostMan" ou tout autre plugin de Safari ou Chrome pour tester des requêtes GET et POST et voir leur résultat, donc tout ça en dehors de Xcode et d'ObjC, pour voir ce que ton serveur de retourne vraiment.
Pourtant je t'assure que quand la requête du profil était faite en synchrone, elle me renvoyais un tableau de valeurs donc je vois vraiment pas le probleme
Et je veut bien les liens vers les sujets traitant de Git stp
J'ai parcouru et regarder la liste des sujets, et ça m'a pas franchement aider à trouver mon bonheur ^^
Hello,
http://pcottle.github.io/learnGitBranching/
https://try.github.io/levels/1/challenges/1
https://www.codeschool.com/courses/try-git
Tu peux travailler aussi avec cet outil pour te faciliter la vie : http://www.sourcetreeapp.com/
et y a un super livre mais j'ai oublié le titre .
Merci et SourceTree j'ai utilisé en début d'année pour le projet iOS donc je connais un peu