29 votes

NSURLConnection vs. NSData + GCD

NSData a toujours eu une méthode très pratique appelée +dataWithContentsOfURL:options:error:. Bien que pratique, elle bloque également l'exécution du thread actuel, ce qui signifie qu'elle était pratiquement inutile pour le code de production (en ignorant NSOperation). J'utilisais cette méthode si rarement que j'avais complètement oublié son existence. Jusqu'à récemment.

La façon dont je récupère des données des tubes est l'approche standard avec NSURLConnectionDelegate : Écrire une classe de téléchargement qui gère les différentes méthodes de NSURLConnectionDelegate, accumuler progressivement des données, gérer les erreurs, etc. Je la rends généralement assez générique pour être réutilisée pour autant de requêtes que possible.

Disons que ma classe de téléchargement typique fait quelque part autour de 100 lignes. C'est 100 lignes pour faire de manière asynchrone ce que NSData peut faire de manière synchrone en une ligne. Pour plus de complexité, cette classe de téléchargement a besoin de son propre protocole délégué pour communiquer l'achèvement et les erreurs à son propriétaire, et ce dernier doit implémenter ce protocole d'une certaine manière.

Maintenant, entrer Grand Central Dispatch, et je peux faire quelque chose d'aussi fantastiquement simple que :

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {

    NSData* data = [NSData dataWithContentsOfURL:someURL];
    // Traiter les données, également de manière asynchrone...

    dispatch_async(dispatch_get_main_queue(), ^(void) {
        // Retour au thread principal pour les mises à jour de l'interface utilisateur, etc.
    });
});

Et je peux insérer ce truc n'importe où je veux, directement. Pas besoin d'une classe de téléchargement, pas besoin de gérer les méthodes de délégué de connexion : Données asynchrones faciles en quelques lignes seulement. La disparité entre cette approche et mon approche avant GCD est d'une ampleur suffisante pour déclencher l'Alarme Trop Beau pour Être Vrai.

Ainsi, ma question : Y a-t-il des inconvénients à utiliser NSData + GCD pour des tâches simples de téléchargement de données au lieu de NSURLConnection (en supposant que je ne me soucie pas de choses comme la progression du téléchargement) ?

22voto

AliSoftware Points 21493

Vous perdez beaucoup de fonctionnalités ici:

  • Vous ne pouvez pas suivre la progression du téléchargement
  • Vous ne pouvez pas annuler le téléchargement
  • Vous ne pouvez pas gérer le processus d'authentification éventuel
  • Vous ne pouvez pas gérer facilement les erreurs, ce qui est vraiment important surtout dans le développement mobile comme sur iPhone bien sûr (parce que vous perdez souvent votre réseau dans des conditions réelles, il est donc très important de suivre de tels cas d'erreur réseau lors du développement pour iOS)

et il y a probablement plus, je suppose.


La bonne approche pour cela est de créer une classe qui gère le téléchargement.

Voir ma propre classe OHURLLoader par exemple, qui est simple et j'ai rendu l'API facile à utiliser avec des blocks:

NSURL* url = ...
NSURLRequest* req = [NSURLRequest requestWithURL:url];

OHURLLoader* loader = [OHURLLoader URLLoaderWithRequest:req];
[loader startRequestWithCompletion:^(NSData* receivedData, NSInteger httpStatusCode) {
    NSLog(@"Téléchargement de %@ effectué (statusCode:%d)",url,httpStatusCode);
    if (httpStatusCode == 200) {
        NSLog(%@"Chaîne reçue: %@", loader.receivedString); // receivedString est un getter pratique qui interprète receivedData en utilisant le codage texte spécifié dans la réponse HTTP
    } else {
        NSLog(@"Code d'état HTTP: %d",httpStatusCode); // Journaliser le code d'état inattendu
    }
} errorHandler:^(NSError *error) {
    NSLog(@"Erreur lors du téléchargement de %@: %@",url,error);
}];

Voir le fichier LIREMOI et le projet d'exemple sur github pour plus d'informations.

De cette manière:

  • vous continuez à utiliser les méthodes asynchrones fournies par NSURLConnection (et comme le dit la documentation d'Apple sur la Programmation Concurrency s'il existe déjà une API pour effectuer des tâches asynchrones, utilisez-la au lieu de vous appuyer sur une autre technologie de threading si possible)
  • vous conservez les avantages de NSURLConnection (gestion des erreurs, etc.)
  • mais vous avez également les avantages de la syntaxe des blocks qui rendent votre code plus lisible que lorsque vous utilisez des méthodes de délégué

12voto

Kazuki Sakamoto Points 10100

Vidéos des sessions WWDC 2010:

  • Session WWDC 2010 207 - Applications Réseau pour iPhone OS, Partie 1
  • Session WWDC 2010 208 - Applications Réseau pour iPhone OS, Partie 2

Le conférencier a dit

"Les Threads Sont Mal™".

Pour la programmation réseau, il est fortement recommandé d'utiliser une API asynchrone avec RunLoop.

Parce que, si vous utilisez NSData + GCD comme ci-dessous, il utilise un thread par connexion.

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
    NSData* data = [NSData dataWithContentsOfURL:someURL];

Et il est probable d'utiliser de nombreuses connexions et de nombreux threads. C'est trop facile d'utiliser GCD :-) Ensuite, de nombreux threads consomment une énorme quantité de mémoire pour leur pile. Il vaut donc mieux utiliser une API asynchrone comme l'a dit AliSoftware.

2voto

mirkokiefer Points 934

À partir de OS X v10.9 et iOS 7, la manière préférée est d'utiliser NSURLSession. Cela vous offre une interface agréable basée sur des blocs et des fonctionnalités comme l'annulation, la suspension et le téléchargement en arrière-plan.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X