CocoaHeads Toulouse - Marc Boudou / FreezySnail - Programmation concurrente
1. Blocks et
programmation
concurrente sous iOS
Marc Boudou
Freezy Snail
Janvier 2012
Monday, January 16, 12
2. Programmation
concurrente
Exécutions de plusieurs tâches en parallèle :
• Tâche en background (UI non bloquant)
• Performance : multiplication des coeurs
Monday, January 16, 12
3. Programmation
concurrente
Exécutions de plusieurs tâches en parallèle :
• Tâche en background (UI non bloquant)
• Performance : multiplication des coeurs
Solutions :
•
Gérer les threads soit même
• Utiliser les frameworks fournis par Apple
Monday, January 16, 12
4. 3 parties
1. Blocks
2. Dispatch Queue : Grand Central Dispatch
3. Operation Queue : NSOperation et
NSOperationQueue
Monday, January 16, 12
5. Blocks
• Extension Objective C
• Depuis iOS 4.0
Monday, January 16, 12
6. Exemple d’utilisation
Suppression d'une vue avec fondu
1 .
[UIView beginAnimations:@"Anim" context:nil];
2 .
[UIView setAnimationDelegate:self];
3 .
[UIView setAnimationDuration:0.6 ];
4 .
[UIView setAnimationDidStopSelector:
@selector(fadeAnimationDidStop:finished:context:)];
5 . myView.alpha = 0;
6 . [UIView commitAnimations];
Monday, January 16, 12
8. Exemple d’utilisation
Suppression d'une vue avec fondu
0.6
myView.alpha = 0;
[myView removeFromSuperview];
Monday, January 16, 12
9. Exemple d’utilisation
Suppression d'une vue avec fondu
0.6
myView.alpha = 0;
[myView removeFromSuperview];
Monday, January 16, 12
10. Exemple d’utilisation
Suppression d'une vue avec fondu
[UIView animateWithDuration:0.6
animations:^{ myView.alpha = 0; }
completion:^(BOOL finished) {[myView removeFromSuperview];}];
Monday, January 16, 12
11. Exemple d’utilisation
Suppression d'une vue avec fondu
[UIView animateWithDuration:0.6
animations:^{ myView.alpha = 0; }
completion:^(BOOL finished) {[myView removeFromSuperview];}];
• Code au même endroit
• Accès direct au contexte
• Blocks : remplace le couple delegate/selector
Monday, January 16, 12
31. FSHttpRequest : .m
@implementation FSHttpRequest
- (void)setCompletionBlock:(BasicBlock)aCompletionBlock
{
! [completionBlock release];
! completionBlock = [aCompletionBlock copy]; // block créé sur
la pile, copy permet de le déplacer sur le tas
}
@end
Monday, January 16, 12
32. FSHttpRequest : .m
@implementation FSHttpRequest
- (void)setCompletionBlock:(BasicBlock)aCompletionBlock
{
! [completionBlock release];
! completionBlock = [aCompletionBlock copy]; // block créé sur
la pile, copy permet de le déplacer sur le tas
}
- (void)setFailedBlock:(ErrorBlock)aFailedBlock
{
[failedBlock release];
! failedBlock = [aFailedBlock copy];
}
@end
Monday, January 16, 12
33. FSHttpRequest : .m
@implementation FSHttpRequest
- (void)setCompletionBlock:(BasicBlock)aCompletionBlock
{
! [completionBlock release];
! completionBlock = [aCompletionBlock copy]; // block créé sur
la pile, copy permet de le déplacer sur le tas
}
- (void)setFailedBlock:(ErrorBlock)aFailedBlock
{
[failedBlock release];
! failedBlock = [aFailedBlock copy];
}
NSURLConnection Delegate :
- (void)connection:(NSURLConnection *)connection
didFailWithError:(NSError *)error
{
! failedBlock(error);
}
@end
Monday, January 16, 12
34. FSHttpRequest : .m
@implementation FSHttpRequest
- (void)setCompletionBlock:(BasicBlock)aCompletionBlock
{
! [completionBlock release];
! completionBlock = [aCompletionBlock copy]; // block créé sur
la pile, copy permet de le déplacer sur le tas
}
- (void)setFailedBlock:(ErrorBlock)aFailedBlock
{
[failedBlock release];
! failedBlock = [aFailedBlock copy];
}
NSURLConnection Delegate :
- (void)connection:(NSURLConnection *)connection
didFailWithError:(NSError *)error
{
! failedBlock(error);
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
! completionBlock();
}
@end
Monday, January 16, 12
36. Utilisation du contexte
Mise à jour d'une ProgressBar lors d'un téléchargement de fichier
Monday, January 16, 12
37. Utilisation du contexte
Mise à jour d'une ProgressBar lors d'un téléchargement de fichier
NSURL * url = [NSURL URLWithString:@"http://www.cocoaheads.fr/toto.zip"];
FSHttpRequest *request = [FSHttpRequest requestWithUrl:url];
[request setBytesReceivedBlock:^(unsigned int size, long long total)
{
}];
[request start];
Monday, January 16, 12
38. Utilisation du contexte
Mise à jour d'une ProgressBar lors d'un téléchargement de fichier
NSURL * url = [NSURL URLWithString:@"http://www.cocoaheads.fr/toto.zip"];
FSHttpRequest *request = [FSHttpRequest requestWithUrl:url];
[request setBytesReceivedBlock:^(unsigned int size, long long total)
{
}];
[request start];
• BytesReceivedBlock appelé lorsqu'on reçoit des données du
serveur (NSURLConnection Delegate : didReceiveData)
Monday, January 16, 12
39. Utilisation du contexte
Mise à jour d'une ProgressBar lors d'un téléchargement de fichier
NSURL * url = [NSURL URLWithString:@"http://www.cocoaheads.fr/toto.zip"];
FSHttpRequest *request = [FSHttpRequest requestWithUrl:url];
[request setBytesReceivedBlock:^(unsigned int size, long long total)
{
label.text = [url path];
}];
[request start];
• BytesReceivedBlock appelé lorsqu'on reçoit des données du
serveur (NSURLConnection Delegate : didReceiveData)
• url : accès par copie
Monday, January 16, 12
40. Utilisation du contexte
Mise à jour d'une ProgressBar lors d'un téléchargement de fichier
unsigned int nbBytesReceived = 0;
NSURL * url = [NSURL URLWithString:@"http://www.cocoaheads.fr/toto.zip"];
FSHttpRequest *request = [FSHttpRequest requestWithUrl:url];
[request setBytesReceivedBlock:^(unsigned int size, long long total)
{
label.text = [url path];
nbBytesReceived += size;
progressView.nbBytesReceived = nbBytesReceived;
progressView.nbBytesTotal = total;
}];
[request start];
• BytesReceivedBlock appelé lorsqu'on reçoit des données du
serveur (NSURLConnection Delegate : didReceiveData)
• url : accès par copie
Monday, January 16, 12
41. Utilisation du contexte
Mise à jour d'une ProgressBar lors d'un téléchargement de fichier
__block unsigned int nbBytesReceived = 0;
NSURL * url = [NSURL URLWithString:@"http://www.cocoaheads.fr/toto.zip"];
FSHttpRequest *request = [FSHttpRequest requestWithUrl:url];
[request setBytesReceivedBlock:^(unsigned int size, long long total)
{
label.text = [url path];
nbBytesReceived += size;
progressView.nbBytesReceived = nbBytesReceived;
progressView.nbBytesTotal = total;
}];
[request start];
• BytesReceivedBlock appelé lorsqu'on reçoit des données du
serveur (NSURLConnection Delegate : didReceiveData)
• url : accès par copie
• nbBytesReceived : accès par référence (modification possible)
Monday, January 16, 12
43. Dispatch Queue : GCD
• Basé sur libdispatch (C library)
• Gère les threads à notre place
• Gère les problèmes de concurrence (lock, ...)
Monday, January 16, 12
44. Dispatch Queue : GCD
• Basé sur libdispatch (C library)
• Gère les threads à notre place
• Gère les problèmes de concurrence (lock, ...)
=> code plus simple à écrire
=> utilisation automatiques des coeurs
=> gestion des ressources processeur optimisée
(création/réutilisation de thread automatique, ...)
Monday, January 16, 12
46. Notion de queue
• Liste FIFO de blocks à exécuter
• Ajout de block dans une queue
• Exécution immédiate du bloc
• Gère les threads qui exécutent les blocks
Monday, January 16, 12
48. Dispatch Queue : 3 types
• Main queue (main thread) : mise a jour de l'UI
• Serial Queue : exécution en série des blocks sur
un seul thread
• Global Queue : exécution en parallèle des blocks
Monday, January 16, 12
49. Serial Queue
Thread courant
Block1 Block2 Block3
Autre Thread
Serial
Queue
Monday, January 16, 12
50. Serial Queue
Thread courant
Block1 Block2 Block3
Création et ajout
de blocks à la Serial
Queue (FIFO)
Autre Thread
Serial
Queue
Monday, January 16, 12
51. Serial Queue
Thread courant
Block2 Block3
Création et ajout
de blocks à la Serial
Queue (FIFO)
Block1
Autre Thread
Serial
Queue
Monday, January 16, 12
52. Serial Queue
Thread courant
Block3
Création et ajout
de blocks à la Serial
Block2
Queue (FIFO)
Block1
Autre Thread
Serial
Queue
Monday, January 16, 12
53. Serial Queue
Thread courant
Création et ajout Block3
de blocks à la Serial
Block2
Queue (FIFO)
Block1
Autre Thread
Serial
Queue
Monday, January 16, 12
54. Serial Queue
Thread courant
Serial queue :
dispatche les blocks à
Création et ajout Block3 un seul thread dans le
de blocks à la Serial même ordre
Block2
Queue (FIFO)
Block1
Autre Thread
Serial
Queue
Monday, January 16, 12
55. Serial Queue
Thread courant
Serial queue :
dispatche les blocks à
Création et ajout un seul thread dans le
de blocks à la Serial même ordre
Block3
Queue (FIFO)
Block2
Autre Thread
Serial
Queue Block1
Monday, January 16, 12
56. Serial Queue
Thread courant
Serial queue :
dispatche les blocks à
Création et ajout un seul thread dans le
de blocks à la Serial même ordre
Block3
Queue (FIFO)
Block2
Autre Thread
Serial
Queue
Monday, January 16, 12
57. Serial Queue
Thread courant
Serial queue :
dispatche les blocks à
Création et ajout un seul thread dans le
de blocks à la Serial même ordre
Queue (FIFO)
Block3
Autre Thread
Serial
Queue Block2
Monday, January 16, 12
58. Serial Queue
Thread courant
Serial queue :
dispatche les blocks à
Création et ajout un seul thread dans le
de blocks à la Serial même ordre
Queue (FIFO)
Block3
Autre Thread
Serial
Queue
Monday, January 16, 12
59. Serial Queue
Thread courant
Serial queue :
dispatche les blocks à
Création et ajout un seul thread dans le
de blocks à la Serial même ordre
Queue (FIFO)
Autre Thread
Serial
Queue Block3
Monday, January 16, 12
60. Serial Queue
Thread courant
Serial queue :
dispatche les blocks à
Création et ajout un seul thread dans le
de blocks à la Serial même ordre
Queue (FIFO)
Autre Thread
Serial
Queue
Monday, January 16, 12
61. Global Queue
Thread courant
Block1 Block2 Block3
Thread core 1 Thread core 2
Global
Queue
Monday, January 16, 12
62. Global Queue
Thread courant
Block1 Block2 Block3
Création et ajout de
blocks à la Global
Queue (FIFO)
Thread core 1 Thread core 2
Global
Queue
Monday, January 16, 12
63. Global Queue
Thread courant
Block2 Block3
Création et ajout de
blocks à la Global
Queue (FIFO)
Block1
Thread core 1 Thread core 2
Global
Queue
Monday, January 16, 12
64. Global Queue
Thread courant
Block3
Création et ajout de
blocks à la Global
Block2
Queue (FIFO)
Block1
Thread core 1 Thread core 2
Global
Queue
Monday, January 16, 12
65. Global Queue
Thread courant
Création et ajout de Block3
blocks à la Global
Block2
Queue (FIFO)
Block1
Thread core 1 Thread core 2
Global
Queue
Monday, January 16, 12
66. Global Queue
Thread courant
Global queue :
dispatche les blocks à
Création et ajout de Block3
plusieurs threads
blocks à la Global
Block2
Queue (FIFO)
Block1
Thread core 1 Thread core 2
Global
Queue
Monday, January 16, 12
67. Global Queue
Thread courant
Global queue :
dispatche les blocks à
Création et ajout de plusieurs threads
blocks à la Global
Queue (FIFO)
Block3
Thread core 1 Thread core 2
Global
Queue Block1 Block2
Monday, January 16, 12
68. Global Queue
Thread courant
Global queue :
dispatche les blocks à
Création et ajout de plusieurs threads
blocks à la Global
Queue (FIFO)
Thread core 1 Thread core 2
Global
Queue Block1 Block3
Monday, January 16, 12
69. Global Queue
Thread courant
Global queue :
dispatche les blocks à
Création et ajout de plusieurs threads
blocks à la Global
Queue (FIFO)
Thread core 1 Thread core 2
Global
Queue Block1
Monday, January 16, 12
70. Global Queue
Thread courant
Global queue :
dispatche les blocks à
Création et ajout de plusieurs threads
blocks à la Global
Queue (FIFO)
Thread core 1 Thread core 2
Global
Queue
Monday, January 16, 12
72. Queue : Création / Utilisation
• Main Queue :
dispatch_queue_t main_queue = dispatch_get_main_queue();
Monday, January 16, 12
73. Queue : Création / Utilisation
• Main Queue :
dispatch_queue_t main_queue = dispatch_get_main_queue();
• Serial Queue :
dispatch_queue_t serial_queue = dispatch_queue_create(@"QueueName", NULL);
...
dispatch_release(serial_queue);
Monday, January 16, 12
74. Queue : Création / Utilisation
• Main Queue :
dispatch_queue_t main_queue = dispatch_get_main_queue();
• Serial Queue :
dispatch_queue_t serial_queue = dispatch_queue_create(@"QueueName", NULL);
...
dispatch_release(serial_queue);
• Global Queue :
dispatch_queue_t global_queue =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
=> LOW, DEFAULT, HIGH
Monday, January 16, 12
75. Exemple :
Calcul background + UI update
Monday, January 16, 12
76. Exemple :
Calcul background + UI update
dispatch_queue_t global_queue=
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
dispatch_async(global_queue, ^
{
// long calcul : transformation XML en HTML avec XSL
NSString * htmlString = [self computeHtml:@"data.xml"];
dispatch_async(dispatch_get_main_queue(), ^
{
[maWebView loadHTMLString:htmlString baseURL:nil];
});
});
Monday, January 16, 12
77. Exemple :
Calcul background + UI update
dispatch_queue_t global_queue=
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
dispatch_async(global_queue, ^
{
// long calcul : transformation XML en HTML avec XSL
NSString * htmlString = [self computeHtml:@"data.xml"];
dispatch_async(dispatch_get_main_queue(), ^
{
[maWebView loadHTMLString:htmlString baseURL:nil];
});
});
• 1er block : ajouté à la Global Queue
• 2e block : ajouté à la Main Queue
Monday, January 16, 12
78. Exemple :
Calcul background + UI update
dispatch_queue_t global_queue=
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
dispatch_async(global_queue, ^
{
// long calcul : transformation XML en HTML avec XSL
NSString * htmlString = [self computeHtml:@"data.xml"];
dispatch_async(dispatch_get_main_queue(), ^
{
[maWebView loadHTMLString:htmlString baseURL:nil];
});
});
• 1er block : ajouté à la Global Queue
• 2e block : ajouté à la Main Queue
• Synchrone : dispatch et attente fin exécution
• Asynchrone : pas d’attente
Monday, January 16, 12
79. Exemple :
Calcul background + UI update
dispatch_queue_t global_queue=
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
dispatch_async(global_queue, ^
{
// long calcul : transformation XML en HTML avec XSL
NSString * htmlString = [self computeHtml:@"data.xml"];
dispatch_async(dispatch_get_main_queue(), ^
{
[maWebView loadHTMLString:htmlString baseURL:nil];
});
});
• 1er block : ajouté à la Global Queue
• 2e block : ajouté à la Main Queue
• Synchrone : dispatch et attente fin exécution
• Asynchrone : pas d’attente
• Rendu HTML => Fin => UI update
Monday, January 16, 12
81. Operation Queue
• NSOperation et NSOperationQueue
• API haut niveau :
• NSOperationQueue : Equivalent Cocoa (Objective-
C based) d'une concurrent Dispatch Queue (C
based)
• N’utilise pas GCD pour l’instant mais multicore
Monday, January 16, 12
82. Operation Queue
Différences avec Dispatch Queue
Monday, January 16, 12
83. Operation Queue
Différences avec Dispatch Queue
• Dispatch Queue : FIFO
• NSOperationQueue plus complexe mais configurable :
- priorités à la volée
- dépendances
- opérations (pause, maxConcurrentOperationCount, ...)
- annulation d’une opération
- ...
Monday, January 16, 12
85. Operation Queue : exemple
• Application avec popup de login : user/password
==> plusieurs requêtes au lancement : une seule popup
Monday, January 16, 12
107. Operation Queue
- (void)start Utilisation
{
// Notifications KVO 'isExecuting'
// Lancement requête
[NSURLConnection connectionWithRequest:self.request delegate:self];
}
- (void)finish
{
// Notification KVO 'isFinished' à la queue :
// Tâche supprimée de la queue, une autre peut être traitée
}
Monday, January 16, 12
108. Operation Queue
- (void)start Utilisation
{
// Notifications KVO 'isExecuting'
// Lancement requête
[NSURLConnection connectionWithRequest:self.request delegate:self];
}
- (void)finish
{
// Notification KVO 'isFinished' à la queue :
// Tâche supprimée de la queue, une autre peut être traitée
}
NSURLConnection Delegate :
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
! if(connexionOK)
! {
! ! queue.maxConcurrentOperationCount = 3;
! }
! completionBlock();
! [self finish];
}
Monday, January 16, 12
109. Operation Queue
Ajouts de GCD : NSBlockOperation
Monday, January 16, 12
110. Operation Queue
Ajouts de GCD : NSBlockOperation
• Opérations simple : utilisation de blocks avec les
NSOperationQueue :
NSOperationQueue * queue = [[NSOperationQueue alloc] init];
NSBlockOperation * blockOp = [NSBlockOperation
blockOperationWithBlock:^{ // Some code }];
[queue addOperation:blockOp];
[queue addOperationWithBlock:^{ // Another Block }];
[queue release];
Monday, January 16, 12
111. Operation Queue
Ajouts de GCD : NSBlockOperation
• Opérations simple : utilisation de blocks avec les
NSOperationQueue :
NSOperationQueue * queue = [[NSOperationQueue alloc] init];
NSBlockOperation * blockOp = [NSBlockOperation
blockOperationWithBlock:^{ // Some code }];
[queue addOperation:blockOp];
[queue addOperationWithBlock:^{ // Another Block }];
[queue release];
• Dépendance entre Operations :
NSBlockOperation * op1 = ...;
NSBlockOperation * op2 = ...;
[op2 addDependencie:op1];
Monday, January 16, 12