iOS/Obj-C : utilisation de GCD


28 septembre 2013

Le but de GCD est de permettre de programmer en "multi-thread" de façon simple et en adéquation avec les capacités de l'appareil qui exécute le code (Un Mac Pro est un poil plus performant qu'un iPhone 3GS...).

J'écris multi-thread entre guillemets parce que GCD utilise le concept de Queue qui est une abstraction d'une thread : parfois c'est une thread, parfois c'en est plusieurs, parfois c'est la thread principale.

Ainsi, pour illustrer de manière concise une utilisation de GCD, imaginons la situation suivante : des petites tuiles d'une grosse image doivent être chargées de manière asynchrone et sans ralentir la thread principale de l'application. On va donc créer une Queue pour le décodage qui se chargera de mettre l'UI à jour avec ce qu'elle a décodée.

self.filesList = [self.archive filesList];
self.fileIdx = 0;
self->Queue_Test1 = dispatch_queue_create("tileLoadingQueue", DISPATCH_QUEUE_CONCURRENT);       //DISPATCH_QUEUE_CONCURRENT / DISPATCH_QUEUE_SERIAL
TestArchive*    Self = self;
double delayInSeconds = 0.02;       //Si on met un délai nul (0.0) la mémoire occupée par les NSData et UIImage (autorelease pour les deux) n'aura jamais l'occasion de se libérer !
//
self->Test1_block = [^{
    NSString*   FileName = [Self.filesList objectAtIndex:Self.fileIdx];
    //On charge et on décode l'image sur la thread auxiliaire
    NSData* ImageDatas = nil;
    if ((ImageDatas = [Self.archive extractFileByName:FileName]) != nil)        {
        UIImage* TargetImage = [UIImage imageWithData:ImageDatas];
        CGImageRef  TargetCGImage = [TargetImage CGImage];
        //On affecte l'image à son layer cible sur la thread UI
        dispatch_sync(dispatch_get_main_queue(), ^()        {
            Self.imageView.layer.contents = (id) TargetCGImage;
        });
        [self fpsStats];
    }
    //On planifie le chargement de l'image suivante (après un certain délai qui permet de libérer la mémoire allouée juste avant)
    Self.fileIdx++;
    if (Self.fileIdx < [Self.filesList count])      {
        dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
        dispatch_after(popTime, Self->Queue_Test1, Self->Test1_block);
    }
} copy];        //Le block est alloué sur la pile au départ. "copy" est la seule manière de le mettre dans la heap et qu'il ne soit pas désalloué.
//
dispatch_async(self->Queue_Test1, self->Test1_block);

On a donc utilisé beaucoup de fonctions de GCD :

  • dispatch_queue_create : pour créer la queue de décodage.
  • dispatch_async : pour démarrer l'exécution du processus complet de décodage.
  • dispatch_sync : pour affecter de façon SYNCHRONE l'image décodée à sa view dans l'interface utilisateur.
  • dispatch_get_main_queue : permet de cibler la queue principale, qui est celle utilisée par l'UI.
  • dispatch_after : pour continuer l'exécution du processus de décodage. Le délai permet de flusher la mémoire placer dans les pools autorelease.

Schéma interne des classes Objective-C


23 mars 2013

Un aide-mémoire sur les relations entre instances et classes en objective-C, du blog très intéressant de Greg Parker :

Le post correspondant à cette image est également très instructif et explique clairement et rapidement le concept de meta-classes.

On peut noter que CocoaWithLove possède une page sur ce même sujet.

Accueil