Pourquoi utiliser les blocks ?

APAP Membre
mai 2011 modifié dans Objective-C, Swift, C, C++ #1
Bonjour,

Connaitriez-vous un bon article expliquant le pourquoi et l'utilisation des blocks en objective-c?

J'ai trouvé quelques articles en anglais mais ca reste très obscur.

Merci pour votre aide :)
«1

Réponses

  • laudemalaudema Membre
    19:12 modifié #2
    Les blocs sont très utiles pour mettre dans la même méthode ce qui avant nécessitait d'en avoir plusieurs. ça permet de garder ensemble du code qui était autrefois un peu éparpillé et même, en dehors des cas prévus, de mettre une fonction interne à  une méthode si tu en as besoin.
    As tu regardé sur le site developer.apple.com
    A short Practical Guide to Blocks Donne une réponse au pourquoi:

    One obvious motivation for using blocks is that an increasing number of the methods and functions of the system frameworks take blocks as parameters. One can discern a half-dozen or so use cases for blocks in framework methods:

    Completion handlers
    Notification handlers
    Error handlers
    Enumeration
    View animation and transitions
    Sorting

    Et la page Blocks Programming Topics: Using Blocks donnerait plutôt une réponse au comment.

    Que te manque t'il ?
  • muqaddarmuqaddar Administrateur
    mai 2011 modifié #3
    Je place ce message dans la section commune car les blocks sont aussi présents sur iOS depuis iOS 4.x.

    EDIT: mets toi sur la page d'accueil du forum, puis fais une recherche avec "blocks" en haut à  droite, il y a environ une 15aine de discussions où on les évoque dans des cas précis.  ;)
  • FloFlo Membre
    19:12 modifié #4
    Regarde aussi du côté de l'AligatorMe, c'est assez convaincant  :D
  • iSofTomiSofTom Membre
    19:12 modifié #5
    Regarde la vidéo du deuxième sujet (À la découverte de GCD) de la session d'avril des CocoaHeads Rennais:
    http://cocoaheads.fr/2011/05/slides-de-la-session-davril-a-rennes/

    Les 20 premières minutes parlent de pourquoi et comment utiliser les Blocks!
  • tabliertablier Membre
    mai 2011 modifié #6
      :(  Hélas, moi aussi je ne comprend pas vraiment les "blocks" et j'ai du mal à  en voir l'intérêt. Si j'avais à  les définir, je dirais que c'est intermédiaire entre les macros et les méthodes. Bref, malgré différentes lectures conseillées ici et le "CocoaHeads Rennais", je suis loin de les utiliser!!  Heureusement on peut encore vivre sans ça!
  • iSofTomiSofTom Membre
    19:12 modifié #7
    dans 1306337073:

      :(  Hélas, moi aussi je ne comprend pas vraiment les "blocks" et j'ai du mal à  en voir l'intérêt. Si j'avais à  les définir, je dirais que c'est intermédiaire entre les macros et les méthodes. Bref, malgré différentes lectures conseillées ici et le "CocoaHeads Rennais", je suis loin de les utiliser!!  Heureusement on peut encore vivre sans ça!


    Non c'est plus entre une fonction et un objet:
    plus puissant qu'une fonction car un block peut gérer des données, plus léger qu'un objet (à  utiliser, car en réalité un Block est un objet).

    Tu étais aux CocoaHeads Rennais ? ou tu as juste vu la vidéo?
  • BunoBuno Membre
    19:12 modifié #8
    dans 1306337073:

    Heureusement on peut encore vivre sans ça!

    Et dire qu'il y a de "pov' applis" dont l'objectif est d'afficher des données dans des tableViews qui sont compatibles iOS 4.0 et + seulement à  cause des blocks...
  • iSofTomiSofTom Membre
    19:12 modifié #9
    dans 1306338180:

    Et dire qu'il y a de "pov' applis" dont l'objectif est d'afficher des données dans des tableViews qui sont compatibles iOS 4.0 et + seulement à  cause des blocks...


    Tu veux dire à  cause des APIs utilisant les Blocks,
    car les Blocks en eux mêmes ont une compatibilité inférieure à  iOS 4.0 !
  • BunoBuno Membre
    19:12 modifié #10
    vi, bien sûr. J'ai raccourci un peu vite ma phrase  :P
  • tabliertablier Membre
    19:12 modifié #11
    Tu étais aux CocoaHeads Rennais ? ou tu as juste vu la vidéo
    J'habite le Vercors. Vu d'ici, Rennes c'est presque à  l'étranger. Donc j'ai vu le début de la vidéo qui malheureusement montre une image fixe ce qui est indiqué sur le site par:
    L'intervention d'Olivier n'est pas disponible en vidéo. En effet, nous avons eu quelques problèmes de captation de son... Cela ne se re-produira plus  ;) 
        B)   heu, capture ou captation?

    @Buno & Shadow
    Moi pas connaitre IOS et pas vouloir connaitre! Moi, vieux Mac Enthousiaste, commencé avec "Apple IIE" ( ~1985?). Aucun intérêt pour objets inutiles et chers.
  • AliGatorAliGator Membre, Modérateur
    19:12 modifié #12
    La session sur les Blocks et GCD c'est Thomas et Pierre qui l'ont donné, et eux ont une vidéo.
    Ce n'est que ma session qui n'a pas pu être mise en vidéo car pas de son (donc autant filer les slides que de faire une vidéo muette de slides qui avancent)

    Recherche sur PommeDev j'ai parlé pas mal de fois des gros avantages des blocks (même sans parler de GCD). Tant pour les animations des vues que, surtout (c'est là  où je les utilise le plus) pour implémenter des callbacks.
    Et j'en passe.
  • DrakenDraken Membre
    19:12 modifié #13
    dans 1306341167:

    Moi, vieux Mac Enthousiaste, commencé avec "Apple IIE" ( ~1985?). Aucun intérêt pour objets inutiles et chers.


    L'Apple IIe, le truc qui valait 3x le salaire moyen en 1985 ?

  • tabliertablier Membre
    19:12 modifié #14
    L'Apple IIe, le truc qui valait 3x le salaire moyen en 1985 ?
    Ben oui, mais la notion de cher dépend de l'époque et de l'environnement. A l'époque je travaillais depuis 20 ans et j'avais travaillé avec des machins bien plus chers et bien moins performants. Et puis le MacIntosh 128 est sorti des cartons......  et des Mac jusqu'à  maintenant. Mais le Mac ça risque (hélas) d'être bientôt fini! Enfin, j'ai d'autres occupations (voir la photo).

    @Aligator
    Ok, j'ai téléchargé les deux, je vais jeter un oe“il  (rassures toi je le récupère après). Ce qui m'embête, ce sont tout ces iBidules que je ne connais pas et n'ai jamais utilisé!
  • 19:12 modifié #15
    dans 1306353037:

    L'Apple IIe, le truc qui valait 3x le salaire moyen en 1985 ?
    Ben oui, mais la notion de cher dépend de l'époque et de l'environnement. A l'époque je travaillais depuis 20 ans et j'avais travaillé avec des machins bien plus chers et bien moins performants. Et puis le MacIntosh 128 est sorti des cartons......  et des Mac jusqu'à  maintenant. Mais le Mac ça risque (hélas) d'être bientôt fini! Enfin, j'ai d'autres occupations (voir la photo).


    T'es trop sénile  :P
  • AliGatorAliGator Membre, Modérateur
    mai 2011 modifié #16
    Si tu n'es pas à  l'aise avec le code pour iBidules et que tu préfères essayer de comprendre avec une méthode du SDK MacOSX qui utilise les blocks, tu peux regarder NSSavePanel : [tt]- (void)beginSheetModalForWindow:(NSWindow *)window completionHandler:(void (^)(NSInteger result))handler[/tt]

    C'est un exemple typique d'utilisation des blocks, à  savoir pouvoir fournir directement le code de la callback en paramètre lors de l'appel de la méthode. (Sur le même principe que ce que j'ai implémenté dans les 2 exemples iOS cités plus haut, sauf que là  c'est une méthode Apple du SDK)
    Ainsi, avec cette méthode du SDK 10.6, au lieu de devoir passer un didEndDelegate et un didEndSelector et implémenter du code ailleurs dans ton fichier .m pour gérer le retour de ton NSSavePanel et récupérer les chemins de fichiers qui ont été choisis par l'utilisateur (code qui sera donc ailleurs, rendant la lecture du code moins lisible), comme on le faisait avant 10.6 sans les blocks, bah là  tu passes juste directement le code... en paramètre !

    Ainsi, sans les blocks ça donne :
    -(void)saveArray {<br />&nbsp; NSArray* dataToSave = ...; // par exemple, le NSArray à  écrire dans un fichier<br />&nbsp; NSSavePanel* panel = [NSSavePanel panel];<br />&nbsp; // ...<br />&nbsp; // configuration du panel<br />&nbsp; // ...<br />&nbsp; [dataToSave retain]; // nécessaire pour garder le tableau en vie le temps que le savePanel modal tourne<br /><br />&nbsp; [panel beginSheetForDirectory:nil file:nil modalForWindow:self.keyWindow modalDelegate:self didEndSelector:@selector(savePanelDidEnd:returnCode:contextInfo:) contextInfo:(void *)dataToSave];<br /><br />&nbsp; NSLog(@&quot;Le SavePanel est affiché et attend que l&#039;utilisateur choisisse un emplacement de sauvegarde avant d&#039;exécuter le code de savePanelDidEnd:returnCode:contextInfo:&quot;);<br />}<br /><br />// ...<br />// Puis, plus loin dans le code :<br />// ...<br /><br />- (void)savePanelDidEnd:(NSSavePanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo {<br />&nbsp; if (result ==&nbsp; NSCancelButton) return; // l&#039;utilisateur a annulé<br /><br />&nbsp; NSArray* dataToSave = (NSArray*)contextInfo; // récupération du NSArray qu&#039;on avait préparé dans le contextInfo<br /><br />&nbsp; NSString* saveFilePath = [[panel URL] path];<br />&nbsp; [dataToSave writeToFile:saveFilePath atomically:YES]; // écriture du NSArray dans le fichier choisi<br /><br />&nbsp; [dataToSave release]; // pour balancer le retain appelé avant le &quot;beginSheet...&quot;<br />}
    
    Du coup tu peux voir un peu les inconvénients : faut passer un didEndDelegate et un didEndSelector (un peu fastidieux) mais surtout, faut implémenter une méthode à  côté, ailleurs dans le code (pas top pour la lisibilité pour comprendre la logique d'enchaà®nement des actions), et en plus faut gérer le contextInfo si tu veux passer des paramètres pour pouvoir récupérer un objet après l'appel asynchrone à  "beginSheet..." et qu'il soit disponible lorsque le didEndSelector est appelé et penser à  le retain puis à  le release...


    Alors qu'avec les blocks, le code à  écrire est vachement plus compact puisque ça donne :
    -(void)saveArray {<br />&nbsp; NSArray* dataToSave = ...; // par exemple, le NSArray à  écrire dans un fichier<br />&nbsp; NSSavePanel* panel = [NSSavePanel panel];<br />&nbsp; // ...<br />&nbsp; [panel beginSheetModalForWindow:self.keyWindow completionHandler:^(NSInteger result) {<br />&nbsp; &nbsp; if (result == NSFileHandlingPanelCancelButton) return; // l&#039;utilisateur a annulé<br /><br />&nbsp; &nbsp; NSString* saveFilePath = [[panel URL] path];<br />&nbsp; &nbsp; [dataToSave writeToFile:saveFilePath atomically:YES];<br />&nbsp; }];<br /><br />&nbsp; NSLog(@&quot;Le SavePanel est affiché et attend que l&#039;utilisateur choisisse un emplacement de sauvegarde avant d&#039;exécuter le code passé en paramètre à  completionHandler&quot;);<br />}
    
    Plus besoin de didEndDelegate, ni de didEndSelector, plus besoin de gérer toi-même le contexte puisque les blocks gèrent tout seul les objets à  retenir ou relâcher le temps nécessaire où le block en a besoin, et en plus le code est centralisé, tu as le code qui affiche le SavePanel et celui qui est appelé de façon asynchrone plus tard tous les deux centralisés au même endroit, ça éviter de scroller partout dans ton .m pour trouver où est cette satanée méthode didEndSelector, etc.
  • muqaddarmuqaddar Administrateur
    19:12 modifié #17
    Moi j'ai vraiment compris à  quoi ça peut servir... mais je préfère pas savoir comment ça marche sous le capot.  :P
  • AliGatorAliGator Membre, Modérateur
    19:12 modifié #18
    Bah pour utiliser les API déjà  existantes (du SDK Apple ou développées par d'autres), tu t'en fiches de savoir comment ça marche sous le capot !
    Tu utilises juste les nouvelles méthodes permettant de passer directement le code (le "block") en paramètre y'a pas plus de question à  se poser :P Quant à  la réponse à  la question "pourquoi utiliser les blocks" à  l'origine de ce post (plutôt que les anciennes API sans blocks)... je pense qu'elle est évidente avec mon exemple maintenant non ?
  • muqaddarmuqaddar Administrateur
    19:12 modifié #19
    Oui, elle est évidente.
    Moins de code, moins de méthodes : le code au même endroit qui concerne la même chose.

    C'est d'autant plus utile dans les classes qui vont appeler plusieurs OpenPanel ou plusieurs ActionSheet... (qu'il faudrait identifier avec l'ancienne méthode).
  • DrakenDraken Membre
    19:12 modifié #20
    dans 1306353037:

    L'Apple IIe, le truc qui valait 3x le salaire moyen en 1985 ?
    Ben oui, mais la notion de cher dépend de l'époque et de l'environnement. A l'époque je travaillais depuis 20 ans et j'avais travaillé avec des machins bien plus chers et bien moins performants. Et puis le MacIntosh 128 est sorti des cartons......  et des Mac jusqu'à  maintenant. Mais le Mac ça risque (hélas) d'être bientôt fini!

    Moi à  l'époque je n'étais qu'un jeune chiot scolarisé et désargenté, contemplant avec tristesse l'Apple II derrière la vitre des boutiques.  :(

  • laudemalaudema Membre
    19:12 modifié #21
    Et maintenant tu laisses tomber tes iBidules dans la baignoire !
    La vie est une grande roue qui tourne et il faut savoir cultiver notre jardin, en plus ça permet de faire de bonnes soupes  :-*

    Les blocks c'est  bien 
  • DrakenDraken Membre
    19:12 modifié #22
    J'ai bien essayé d'utiliser un Apple II dans ma baignoire, mais ce n'était pas follement pratique.

  • laudemalaudema Membre
    19:12 modifié #23
    J'ai eu besoin d'ajouter un "uid" à  une de mes classes pour pouvoir en garder une référence dans des fichiers textes sur lesquels l'appli travaille. Quand je charge un fichier je recherche dans un tableau un objet (Ordo) par la string uid.

    J'ai voulu trouver la méthode la plus rapide de trouver une Ordo dans un NSArray *ordos (il y en a 258 actuellement)

    voilà  le code en cherchant successivement via
    • une énumération rapide
    • les blocks
    • les predicates

    <br />	if (0 &lt;[ordos count]) {<br />&nbsp; &nbsp; &nbsp; &nbsp; NSArray *uids =[ordos valueForKey:@&quot;uid&quot;];<br />		Ordo *o;<br />		int counter0 = 0, counter1 = 0, counter2 =0, c = [ordos count];<br />		NSTimeInterval debut = [[NSProcessInfo processInfo] systemUptime], dureeBoucle, dureeBlocks, dureePredicates;<br />		for (NSString *oUID in uids){<br />			for ( o in ordos ){<br />				if ( [o.uid isEqualToString:oUID]){<br />					counter0++;<br />					break;<br />				}					<br />			}<br />		}<br />		dureeBoucle = [[NSProcessInfo processInfo] systemUptime]-debut;<br />		debut = [[NSProcessInfo processInfo] systemUptime];<br />		for (NSString *oUID in uids){<br />			NSInteger index = [ordos indexOfObjectPassingTest:<br />							&nbsp;  ^(Ordo* obj, NSUInteger idx, BOOL *stop) {return[obj.uid isEqualToString:oUID];}];<br />			if ( NSNotFound != index )<br />				if ((o = [ordos objectAtIndex:index]))<br />					counter1++;<br />		}<br />		dureeBlocks = [[NSProcessInfo processInfo] systemUptime]-debut;<br /><br />		NSPredicate *predicate, *template = [NSPredicate predicateWithFormat:@&quot;uid == $OUID&quot;];<br />		NSMutableArray *predicates = [[[NSMutableArray alloc]init]autorelease];<br />		debut = [[NSProcessInfo processInfo]systemUptime];<br />		for (NSString *oUID in uids){<br />			[predicates addObject: [template predicateWithSubstitutionVariables:<br />						 [NSDictionary dictionaryWithObject:oUID forKey:@&quot;OUID&quot;]]];<br />		}<br />		NSTimeInterval dureePredicatesArray = [[NSProcessInfo processInfo] systemUptime]-debut;<br /><br />		debut = [[NSProcessInfo processInfo]systemUptime];<br />		for (predicate in predicates){<br />			if ((o = (Ordo*)[[ordos filteredArrayUsingPredicate:predicate]lastObject]))<br />							 counter2++;<br />		}<br />		dureePredicates = [[NSProcessInfo processInfo] systemUptime]-debut;<br />		printf(&quot;à‰num: %.3f count: %i, Blocks : %.3f count: %i, buildingArray : %.3f predicates: %.3f cout: %i - c was %i&#092;n&quot;<br />			&nbsp;  , (float)dureeBoucle, counter0, (float)dureeBlocks, counter1, (float)dureePredicatesArray, (float)dureePredicates, counter2, c);<br />&nbsp; &nbsp; }<br />
    

    Le tableau de prédicats est construit dans une boucle séparée pour ne pas imputer à  la recherche le temps de création. ça ne sert à  rien c'est pas là  que je perds vraiment du temps ...

    Enum: 0.009 count: 258, Blocks : 0.010 count: 258, buildingArray : 0.001 predicates: 0.061 cout: 258 - c was 258

    Première conclusion: ça va tellement vite qu'on ne voit pas la différence à  l'oeil nu quelque soit la méthode !
    Seconde conclusion: énumération + break < blocs < predicates..

    Si les blocks sont un chouà¯a plus lents que l'énumération rapide ils sont plus rapides à  écrire et relire (quand on a pris l'habitude) et bien plus rapides que les predicates..
  • 19:12 modifié #24
    Les predicates c'est l'horreur avec des bases de données assez grosses.
  • muqaddarmuqaddar Administrateur
    mai 2011 modifié #25
    En tout cas, un bête predicate pour filtrer un array de résultats est déjà  10 fois plus rapides qu'une clause "LIKE %search%" dans une requête SQL (si les données proviennent donc d'une base).

    NSPredicate *predicate = [NSPredicate predicateWithFormat:@&quot;(SELF contains[cd] %@)&quot;, searchText];
    
  • FloFlo Membre
    19:12 modifié #26
    Et pourquoi ne pas plutôt construire un NSDictionary plutôt qu'un NSArray ?
    Cette classe a été spécialement implémentée pour effectuer, entre autres, des cherches d'un objet à  partir d'une clé  :D
  • laudemalaudema Membre
    19:12 modifié #27
    Parce que le dictionnaire se substituerait à  la classe, pas au tableau !
    Mais effectivement un moment je me suis demandé si je n'aurais pas pu/dû faire plutôt des NSDictionaries que des Class, avec les catégories on doit pouvoir leur ajouter des méthodes ....
  • AliGatorAliGator Membre, Modérateur
    19:12 modifié #28
    Dans ce cas il vaut mieux implémenter le pattern Composition (ou à  la limite Héritage) que faire des catégories, en terme de conception/architecture du programme.

    Mais en effet, si tes "ordos" sont identifiées de façon uniques pas un uid, et qu'en plus tu manipules souvent cet uid, plutôt que stocker tes objet Ordo dans un NSArray, j'aurais stocker ces objets Ordo (donc tu gardes toujours des classes), dans un NSDictionary, indexé par leur uid (c'est ce pense ce que voulais dire Flo, pas remplacer tes classes par des NSDictionary, mais stocker ces instances de classes dans un NSDictionary pour avoir un accès indexé à  ces objets).

    Ainsi tu aurais [tt]Ordo* o = [ordos objectForKey:uid];[/tt] pour récupérer un objet Ordo en connaissant son UID. Et bien évidemment, cet objet [tt]Ordo* o[/tt] aurait sa propriété o.uid qui vaudrait l'UID demandé, celui correspondant  la clé... mais aurait aussi lui toutes les autres propriétés.

    PS j'ai pas trop compris ton algo avec les NSPredicates, si tu veux juste ordonner un ensemble d'objet Ordo par uid ?!
  • FloFlo Membre
    19:12 modifié #29

    (c'est ce pense ce que voulais dire Flo, pas remplacer tes classes par des NSDictionary, mais stocker ces instances de classes dans un NSDictionary pour avoir un accès indexé à  ces objets)


    ouep  ;)
  • laudemalaudema Membre
    19:12 modifié #30
    dans 1306848290:

    Mais en effet, si tes "ordos" sont identifiées de façon uniques pas un uid, et qu'en plus tu manipules souvent cet uid, plutôt que stocker tes objet Ordo dans un NSArray, j'aurais stocker ces objets Ordo (donc tu gardes toujours des classes), dans un NSDictionary, indexé par leur uid (c'est ce pense ce que voulais dire Flo, pas remplacer tes classes par des NSDictionary, mais stocker ces instances de classes dans un NSDictionary pour avoir un accès indexé à  ces objets).

    J'utilise ces Ordo* dans d'autres cas et je ne me sers (pour l'instant) de leur uid que pour faire le lien avec des objets Facture* que je recrée à  partir de fichiers générés par une application tierce d'après les renseignements que je fournis.
    Les ordos n'ont pas qu'un lien avec des factures mais aussi avec des (ABPerson*) patients, des prescripteurs, des rendez vous iCal. des copies scannées sur le disque... Un dictionnaire ne serait pas pratique pour gérer tout ça !

    dans 1306848290:

    Ainsi tu aurais [tt]Ordo* o = [ordos objectForKey:uid];[/tt] pour récupérer un objet Ordo en connaissant son UID. Et bien évidemment, cet objet [tt]Ordo* o[/tt] aurait sa propriété o.uid qui vaudrait l'UID demandé, celui correspondant  la clé... mais aurait aussi lui toutes les autres propriétés.

    Il va falloir que je creuse cette piste qui me semble très prometteuse. Pour les factures un dictionnaire suffirait bien en effet et m'éviterait d'avoir à  charger l'ordo pour mes états de sortie.. Il suffirait d'un NSArray des clefs pour créer un dictionnaire à  la volée ..

    dans 1306848290:

    PS j'ai pas trop compris ton algo avec les NSPredicates, si tu veux juste ordonner un ensemble d'objet Ordo par uid ?!

    Le predicate ne peut ramener qu'une ou zéro ordo car une uid n'existe pas dans plus d'une ordo, en demandant lastObject j'ai nil ou la seule ordo qui m'intéresse. C'est mieux que faire objectAtIndex:0 si le tableau est vide :)
  • AliGatorAliGator Membre, Modérateur
    19:12 modifié #31
    Ok j'avais pas compris que tu avais du 1-* dans ton modèle, je pensais que c'était du 1-1 et que tu ne cherchais qu'à  trier tes Ordo par ordre de uid, sans forcément de relation avec une facture ou autre.

    Bon après le backend de ton modèle c'est quoi ? Une base SQLite ? du CodeData ?
    Parce que pour tout l'aspect relationnel, ça peut valoir le coup de se poser la question de plutôt faire une requête avec des jointures plutôt que de gérer les jointures côté modèle en RAM ?
Connectez-vous ou Inscrivez-vous pour répondre.