6 votes

Questions sur la GCD iOS 4

J'ai regardé quelques présentations de WWDC 2010 et lu la plupart des documents sur les blocs et la concurrence et j'ai quelques questions concernant l'utilisation de blocs avec des files d'attente en série dans Grand Central Dispatch. J'ai un projet iOS 4 qui a une vue de défilement et un dictionnaire d'informations sur les images - url des images, etc. Je veux utiliser GCD et des blocs pour télécharger les images et les mettre dans mon scrollview sans bloquer le thread principal. J'ai écrit le code suivant qui semble fonctionner :

for (NSDictionary* dict in images)
{
     dispatch_async(image_queue, ^{

           NSString* urlString = [dict objectForKey:@"url"];
           NSURL* url = [NSURL URLWithString:urlString];
           NSData* imageData = [[NSData alloc] initWithContentsOfURL:url];
           UIImage* image = [UIImage imageWithData:imageData];
           UIImageView* imageView = // initialize imageView with image;      

           dispatch_async(dispatch_get_main_queue(), ^{
                [self.scrollView addSubview:imageView];
           });
           [imageData release];
      });
}

J'ai deux questions à poser :

  1. D'après le guide des concurrences, je ne devrais pas capturer des variables de la portée englobante qui ne sont pas de type scalaire - dans mon code, je capture dict qui est un objet NSDictionary*. Si je ne suis pas autorisé à la capturer, comment dois-je alors écrire le code ? Un bloc ne capture-t-il que les variables de l'espace environnant qui sont effectivement utilisées ?

  2. Que se passe-t-il si je quitte le ViewController actuel avant que toutes les images ne soient récupérées dans la file d'attente de la série ? Je ne pense pas qu'ils soient conscients que le ViewController qui les a créés a disparu, alors que se passe-t-il lorsqu'ils exécutent le completion handler où j'insère les vues d'images dans ma scrollview sur le thread principal ? Est-ce que cela provoque une erreur ou quoi ? Et comment puis-je annuler toutes les opérations restantes sur la file d'attente série lorsque mon ViewController disparaît ?

Je vous prie d'agréer, Madame, Monsieur, l'expression de mes salutations distinguées,

10voto

Kaelin Colclasure Points 2746
  1. Bien qu'il s'agisse d'un point délicat, il est important pour comprendre ce que le guide sur la concurrence essaie de vous dire : Pointeurs sont les types scalaires. Vous pouvez donc capturer des pointeurs à l'intérieur de blocs autant que vous voulez... MAIS vous devez être conscient de la durée de vie de la mémoire vers laquelle ils pointent ! NSDictionary * est une sorte d'id, et lorsque vous faites référence à un id dans un bloc, le runtime prend la responsabilité de conserver l'id si le bloc est copié (ce qui est le cas par dispatch_async()) et de le libérer lorsque le bloc lui-même est désalloué. Et oui, un bloc ne capture que les variables qui y sont référencées.

  2. Puisque vous savez maintenant que le bloc asynchrone a effectué un retain sur soi, il devrait être clair que (sauf erreur de gestion de la mémoire) votre ViewController ne peut pas "disparaître" tant que le bloc n'est pas terminé. Il ne va donc pas se planter -- mais vous avez raison de noter que vous avez vraiment besoin d'un moyen d'annuler ce type de travail asynchrone lorsque vous n'avez plus l'intention d'utiliser les résultats. Un modèle simple mais efficace est de mettre un test au début de votre bloc asynchrone qui vérifie si le travail doit toujours être effectué.

3voto

Puisque d'autres ont répondu à vos deux questions, je voudrais commenter votre code et vous recommander de ne pas utiliser GCD pour les requêtes de réseau comme les images. Le principal problème est qu'elles s'exécutent toutes simultanément. En fonction du nombre de téléchargements, vous risquez de créer beaucoup trop de connexions simultanées qui peuvent bloquer une connexion cellulaire, et l'utilisateur peut finir par penser que quelque chose ne va pas si les images ne commencent pas à apparaître alors qu'il est en train de se battre pour le précieux réseau.

Essayez d'utiliser un NSOperationQueue avec un maxConcurrentOperationCount de 2 ou 3. Cela vous permettra de mettre en file d'attente un nombre potentiellement infini de requêtes réseau, mais d'en faire exécuter quelques-unes au maximum en parallèle. Puisque vous pouvez accéder à l'état du réseau de l'appareil, vous pouvez conditionnellement augmenter cette valeur à 8 par exemple pour les connexions wifi.

Le deuxième problème du GCD est qu'il est un peu difficile d'annuler les opérations en cours. Si votre utilisateur entre dans un contrôleur de vue et le repousse ensuite, selon la façon dont vous avez programmé votre code, les blocs GCD retiendront le contrôleur de vue, l'empêcheront d'être libéré et, en fait, toutes les opérations de réseau devront se terminer jusqu'à ce que le contrôleur puisse être libéré (il n'est donc pas utile d'annuler les connexions en dealloc ).

Je l'ai appris à mes dépens en surveillant un proxy et en allant rapidement à l'intérieur d'une vue et en revenant. C'était vraiment effroyable de voir comment les connexions en attente a causé environ 20 secondes de dommages supplémentaires à la bande passante du réseau à ma demande qui, par ailleurs, n'était pas consciente.

tl;dr utiliser une file d'attente pour le réseau, GCD pour le traitement de l'image/la mise à l'échelle/la mise à jour de l'interface utilisateur.

1voto

Artem Oboturov Points 2292

@Kaelin Colclasure : pour la première question, il semble plus probable qu'il s'agisse d'un problème d'état partagé dans une application multithread : pour un type intégral, vous avez une copie par valeur (qui s'applique également aux pointeurs), mais lorsque vous utilisez un objet référencé par un pointeur, vous rencontrez tous les problèmes liés à l'absence de verrouillage (une sorte de variation du problème de durée de vie de l'objet que vous avez mentionné ici).

0voto

Stephen Darlington Points 33587

Voici la différence pratique pour votre premier point :

Si je passe un scalaire dans un bloc utilisé dans une file d'attente GCD, je travaille à l'intérieur du bloc avec une copie des données originales. Les modifications ne seront pas visibles en dehors du bloc. (Il y a des mises en garde à ce sujet, le __block par exemple, mais en général c'est correct).

Si je passe, par exemple, un NSMutableDictionary dans un bloc, modifie le dictionnaire volonté sont visibles à l'extérieur du bloc - ceci parce que vous avez conservé un référence au dictionnaire et n'a pas pris une copie profonde.

Dans les deux cas, la gestion de la mémoire est effectuée pour vous, c'est-à-dire que la variable scalaire est copiée ou que les objets sont conservés.

Puisque vous ne pouvez pas modifier le contenu d'un NSDictionary une fois qu'il a été initialisé, vous constaterez probablement que les blocs font automatiquement ce qu'il faut pour vous.

En ce qui concerne le deuxième point, la gestion de la mémoire est pratiquement automatique, sauf si vous devez travailler sur une copie d'un objet mutable.

0voto

Arun chauhan Points 1

Il existe certaines limitations et exigences à prendre en compte lors du développement de BLE pour iOS. La première concerne le simulateur iOS. Il est intéressant de noter qu'à un moment donné, le simulateur iOS prenait en charge le développement Bluetooth (la vidéo Bluetooth 101 de la WWDC 2012 fait référence à cette fonctionnalité), mais à la WWDC 2013, Apple a annoncé que le simulateur ne prendrait plus en charge le Bluetooth. À première vue, cela semble regrettable. Cependant, le développement sur un appareil est une expérience meilleure et plus précise avec BLE. De plus, comme tous les iPhones fabriqués après l'iPhone 4s (2011) sont équipés de Bluetooth 4.0 - et par extension, de BLE - la plupart des développeurs iOS ont déjà des appareils qui le prennent en charge. Il n'est presque plus nécessaire de poser la question, mais il est bon de savoir qui peut l'utiliser au cas où vous auriez un ensemble limité de types d'appareils pour lesquels vous développez des applications ou des appareils BLE.

Une autre exigence importante à garder à l'esprit lors du développement de Core Bluetooth est qu'Apple place la majorité de la responsabilité des interactions avec les appareils BLE sur le développeur de l'application. Très peu de choses sont gérées et maintenues par iOS lui-même en ce qui concerne la gestion de Bluetooth. Une chose qui est gérée dans le système d'exploitation est la connexion qui apparaît dans l'application Réglages > Bluetooth.

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