Tutoriel : La programmation Mac


<>



Tutoriel : La programmation Mac

Table des matières

La programmation Mac

Bonjour à tous, j’ai enfin décidé de me (re)mettre à la rédaction d’un tutoriel pour le SdZ sur la programmation Macintosh !

Mais qu’est-ce que t’entends par programmation Mac ?



Bah, en fait, j’entends par là la programmation sur la plate-forme Mac OS X dans son ensemble, à commencer par le langage : Objective-C, on peut bien sûr programmer dans beaucoup d’autres langages mais celui-ci est mon préféré 😀 . C’est surtout c’est celui qu’Apple pousse le plus… C’est-à-dire que c’est celui qui a le plus de chances de résister au temps chez Apple, et donc d’être le mieux fourni en API compatibles…

En parlant d’API, je vais, pour commencer, parler de l’API Cocoa et ce, pour les mêmes raisons que pour le langage : c’est celle qu’Apple aide le plus. Mais je vais aussi vous apprendre à utiliser les outils fournis gratuitement par Apple pour développer sur Mac. Ces outils se nomment Xcode et Interface Builder dans un premier temps, ce sont ces outils utilisés pour développer nos chères applications .app;) . Donc, de beaux outils de pro à la portée de tout un chacun !

Dans un premier temps, ce sera ça… Mais si vous êtes sages, je vous montrerai quelques petits cadeaux d’Apple du style de CoreGraphics et autres joyeusetés 😀 .

Mais quelle impolitesse de ma part… J’ai oublié de me présenter : je m’appelle Psycho (Rémy en fait, mais tout le monde m’appelle Psy, alors…), je suis Macmanique depuis l’âge de 10 ans (j’en ai 21), étudiant en informatique sur PC ; la programmation sur Mac est pour moi un passe-temps et surtout m’évite de péter les plombs à force d’écrire du C# débile ^^ {troll}. Et comme j’aime bien partager ce que j’apprécie avec les autres, j’ai décidé de faire ce petit tutoriel sur un environnement de développement qui n’est limité que par votre imagination… Et vos compétences 😀 .

Je crois que c’est tout pour l’instant… J’espère que vous allez être tout aussi passionnés que moi en utilisant cet environnement de développement complet !
Attachez vos ceintures, bon courage… C’est parti !!!

Pour commencer…

Dans cette partie, je vais commencer par exposer la base C de l’Objective-C, brièvement, M@teo le fait très bien lui-même. En ajoutant à cette base ce qui est spécifique à l’Objective-C.

Je vais aussi vous exposer les plus gros concepts de la programmation objet selon Objective-C… Plusieurs de ces concepts ont été récupérés par d’autres langages mais peut-être pas poussés aussi loin, bien que finalement, pour le programmeur ça ne change pas grand-chose 😀 . Il vaut cependant mieux savoir comment ça fonctionne sous le capot pour éviter de faire des conneries 😀 .

 

La base C

Le C est-il vraiment compatible avec Objective-C ? Tout ce que j’ai fait avec le tutoriel de M@teo, je peux le faire en Objective-C sans rien changer ?

Oui ! Et mille fois oui ! Contrairement au C++ qui est un langage complètement différent inspiré du C. L’Objective-C est fait de C et de classes. Mais il faut tout de même apporter des modifications mineures pour faire passer vos codes C pour du code Objective-C.

Déjà, comme en C, on a deux types de fichiers : les fichiers sources .c et les fichiers d’en-tête (header) .h. En Objective-C, le fichier source devient un fichier .m au lieu de .c et le .h reste ce qu’il est.

Ensuite, parmi les directives de précompilations comme #include, #define, #ifndef, etc., l’Objective-C ajoute la directive #import. Cette directive est très pratique. En effet, elle remplace #include et permet, dans le fichier importé, de ne pas mettre les directives empêchant l’inclusion multiple à savoir :

#ifndef FICHIER_H_
#define FICHIER_H_

//déclarations

#endif

Vous n’en avez plus besoin !!! Le compilateur gère lui-même l’inclusion avec la directive #import et empêche d’inclure à nouveau un fichier déjà importé sans ces directives.

L’Objective-C ajoute lui aussi son type booléen, il est un peu différent, il s’agit en fait du type BOOL, et ses valeurs possibles sont YES (oui / vrai) et NO (non / faux), qui sont comme en C lorsqu’on définit un type booléen : NO = zéro, et YES = tout sauf zéro. Sinon, pour le reste, tout est à peu prés pareil… En tout cas, dans la programmation procédurale.

Les fonctions, les structures et les variables doivent toujours être déclarées avant d’être utilisées. Faites attention aux abus du C++. Vous ne pouvez notamment pas surcharger les opérateurs, ni les fonctions normales.
La nomenclature est toujours la même : quand il y a plusieurs mots dans une variable, on les identifie en mettant une majuscule à chacun d’eux, exemple : maVariable ; les noms des nouveaux types portent des majuscules au début. Les variables constantes sont en majuscules… vous pouvez bien sûr ne pas faire tout ça, mais c’est conseillé…
Autre chose qui sera un peu plus importante au niveau de la programmation objet, c’est d’éviter les underscores « _ » dans les noms des fonctions : il est préférable en effet de garder les majuscules pour les nouveaux mots, plutôt que de mettre l’underscore à cause des conversions faites en interne par le compilateur… Mais je vous expliquerai ça plus tard 😀 .

Voilà donc pour la programmation procédurale. La programmation procédurale est, au niveau du système, inchangée : une fonction est exécutée exactement de la même façon lors de son appel, c’est-à-dire qu’un espace lui est fourni dans la mémoire, et les valeurs des arguments y sont copiées, etc. Vous aurez donc besoin des pointeurs, et ça à la pelle : vous ne pourrez vraiment pas faire sans, il n’y a pas de références comme en C++. Vous devrez même gérer la mémoire vous-mêmes, il n’y a pas de garbage collector comme en Java, mais ne vous inquiétez pas, ce n’est pas particulièrement compliqué.


La métaphore des messages

Alors voilà : le concept Objet, aujourd’hui très répandu, est comme en C++ ou Java et les autres, il a pour principe de réunir dans une unique structure homogène les données et les fonctions qui y sont attachées… Ça ne change pas ici 😀 . En l’occurrence, c’est plutôt la façon d’écrire une classe qui change. Mais avant de vous dire comment écrire une classe, je vais vous expliquer le concept central de la programmation en Objective-C.

Le dynamisme

L’Objective-C est un langage entièrement dynamique. Il est basé sur un runtime (système d’exécution) qui permet de gérer les appels des messages, la création des objets, la communication entre eux… C’est ce qui a inspiré la machine virtuelle du Java, qui permet, pour le java, une portabilité totale : il faut juste avoir la machine virtuelle en question installée sur sa bécane 😀 . En Objective-C, c’est à peu près pareil, mais si vous avez GCC installé sur votre machine (ce qui est le cas quand vous installez Xcode sur Mac), vous avez cette machine virtuelle.

Cependant, le runtime de l’Objective-C permet un dynamisme beaucoup plus fort qu’en Java, en laissant le soin au runtime d’exécuter telle ou telle méthode selon le contexte. En effet, en Objective-C, seul le runtime se soucie des types et des redirections, au pire le compilateur vous donne un warning ; en effet, le type d’un objet n’est connu qu’à l’exécution.

On ne connaît pas le type au moment de la compilation ? Comment on fait quand on exécute la méthode d’un objet ? Comment on sait quelle méthode on peut exécuter ?



Déjà, je suppose que vous savez ce que vous faites : donc, vous connaissez les méthodes que vous pouvez exécuter sur un objet à tel moment. À part ça, le type n’est pas directement connu à la compilation, il est possible d’indiquer quel est le type d’un objet, mais vous n’y êtes pas forcés et pour le compilateur, ça ne changera pas grand-chose. Comme je l’ai dit, le compilateur vous dira au pire que l’objet ne pourra peut-être pas exécuter la méthode… Pas grand-chose, quoi 😀 , une simple supposition. Sinon, c’est le runtime qui s’occupe de « reconnaître » un objet et de lui faire exécuter une méthode. C’est possible parce que chaque objet est capable de s’identifier, de dire au runtime à n’importe quel moment à quelle classe il appartient, quelle est sa classe mère et quelles méthodes il est capable d’exécuter.

Les messages

Ce système est notamment permis par la métaphore du message. Ici, contrairement à ce que je dis depuis tout à l’heure, on n’appelle pas des méthodes, mais on envoie un « message » à un objet. Pour le programmeur, ça ne change strictement rien (sauf quand on l’écrit mais ça, c’est une autre histoire 😉 ), si l’objet peut exécuter la méthode, elle est exécutée… En revanche, ce qui change des autres langages, c’est que si l’objet n’en est pas capable, soit l’objet redirige le message, soit le runtime laisse tomber l’exécution de la méthode 😀 . Ça permet d’éviter de s’inquiéter pour des méthodes non gérées ; dans la majorité des cas, le programme continuera son exécution sans problème. Ici, le concept d’objet et de message est poussé au point que même les classes sont des objets : on les appelle « objets classe » et ils sont simplement identifiés par le nom de la classe lui-même. L’objet classe peut lui aussi recevoir des messages et exécuter des méthodes, mais il a surtout la capacité de créer de nouveaux objets de sa classe selon le modèle que vous lui avez fourni. Il alloue de la mémoire pour stocker l’objet et renvoie l’ « instance » de la classe pour que vous puissiez l’utiliser. Cet objet peut à présent recevoir des messages comme les autres.

Et concrètement, comment ça se passe ?

Il s’agit d’un système de redirection : quand on envoie un message à un objet, le runtime cherche dans les registres de l’objet pour voir s’il peut exécuter la méthode et ensuite lui fait exécuter la méthode. En java, les messages ont été repris, mais là il faut connaître le type de l’objet à la compilation pour être capable de lui envoyer un message. Sachant qu’en ObjC vous n’avez pas besoin de connaître le type de l’objet pour lui envoyer un message, vous pouvez donc à ce moment définir des classes et des méthodes prenant un objet quelconque en argument et permettre à d’autres programmeurs d’utiliser votre classe avec leurs propres objets ! C’est pratique : par exemple, pour les classes conteneurs comme les listes, les tableaux ou les dictionnaires.

Si la personne qui utilise votre classe veut utiliser certaines de ses capacités, il lui suffit de définir les méthodes utilisées par votre classe dans sa propre classe : de votre côté, vous envoyez des messages à des objets sans types particuliers, l’objet répondra ou non et vous modifierez le comportement selon le résultat. Je sais que ça a l’air banal comme ça… mais en fait, c’est très puissant et ça vous enlève quelques poids, vous n’avez pas besoin de classes génériques comme en C++ par exemple, vous n’avez pas non plus besoin de faire des casts sur les objets avant de leur envoyer un message comme en Java : on laisse au runtime le soin de connaître le type des objets et d’exécuter les méthodes.

Voilà tout pour les cours magistraux sur le minimum à savoir sur l’Objective-C avant de commencer 😀 .
On va passer à des trucs plus concrets et les cours magistraux seront dispensés tout au long du tutoriel.
Si vous avez un peu de mal avec ce que je viens de dire, ne vous en faites pas trop, c’est juste de la théorie, dans la pratique tout est transparent pour le programmeur.


 

Votre premier programme Objective-C

Nous allons commencer par utiliser, plus que concevoir, un nouveau programme avec un nouveau projet Xcode que je vais vous présenter brièvement. Et vous allez pouvoir constater la façon très différente de faire des appels de méthodes en Objective-C.

 

L’outil : Xcode

Avant de commencer à programmer, on va prendre nos outils en main… À savoir Xcode pour l’instant 😀 ; l’interface graphique, c’est pour plus tard 😉 .

Le choix du projet

Donc, si vous avez lu le cours de M@teo incluant la partie sur l’IDE, vous connaissez déjà ce logiciel… Donc pas besoin de revenir là-dessus 😀 . On va créer un nouveau projet Objective-C :

  • faites File –> New Project… ou Cmd + Maj + N 😀 ;

  • sélectionnez dans la partie Command Line Utility le type Foundation Tool puis cliquez sur Next ;

  • donnez un nom à votre projet ; pour moi, ce sera TutorielOC ; choisissez-lui un emplacement sur le disque et cliquez sur Finish !

Et c’est bon, votre fenêtre de projet apparaît…

Un petit ajustement

Bon : comme je n’ai pas envie de trop m’emmerder avec les déclarations de variables, je vais vous présenter une petite option bien pratique. Votre première modification d’IDE. 😀
Donc, dans votre fenêtre de projet :

  • cliquez sur le nom de votre projet dans la liste à gauche, c’est le premier élément ;

  • faites File –> Get Info… ou plus simplement Cmd + I ;

  • cliquez sur la liste déroulante Collection et choisissez Language (vous pouvez sauter cette étape, mais ça permet d’éclaircir la liste qui est juste en dessous 😀 ) ;

  • À côté de C Language Dialect, cliquez sur Compiler Default et choisissez C99 [-std=c99] » ;

  • fermez cette petite fenêtre…

Ce que vous venez de faire, c’est configurer votre compilateur GCC, il est possible de tout modifier grâce à Xcode, de configurer tout ce que vous voulez… Vous pouvez regarder les options disponibles. Pour l’instant, celle que je vous ai montrée nous suffit, elle met en fait le compilateur à la norme C99, ce qui nous permet notamment de déclarer les variables à la volée, c’est-à-dire au moment où on en a besoin, et non pas au début du programme, comme ce n’est pas censé être le cas en C traditionnel. Cela nous permet aussi les boucles for de ce genre : for(int i=0;…, ce qui est plus pratique 😀 .

Les premiers fichiers de votre projet

Pour l’instant, vous n’avez écrit aucune ligne de code, votre projet contient déjà 5 fichiers et « TutorielOC » dans la liste de gauche est sélectionné. Ce que vous avez pour l’instant, c’est :

  • Foundation.framework : on a sélectionné un projet de type Foundation Tool ; donc, ce framework contient les outils par défaut nécessaires à notre projet ;

  • TutorielOC : son nom est écrit en rouge, ça signifie juste que le fichier est référencé mais qu’il n’existe pas ; en fait, il s’agit de votre exécutable et il sera créé dés la première compilation ;

  • TutorielOC.1 : un fichier MAN pour écrire la documentation sur votre outil… On ne l’utilisera pas ;

  • Tutoriel.m : un fichier source Objective-C comme je vous l’avais dit ; quand vous ferez un projet graphique avec Cocoa, ce fichier s’appelera main.m, il contient donc la définition de la fonction main() permettant de commencer le programme. C’est ici que tout va commencer pour notre tutoriel !

  • TutorielOC_Prefix.pch : un fichier d’en-tête pour inclure tous les fichiers nécessaires à notre exécutable, on n’y touchera pas non plus.

Voilà tout pour le projet lui-même, nous allons maintenant faire votre premier programme.




 

Votre premier programme !

C’est parti… Compilons le programme pour rigoler 😀 . Cliquez sur le bouton Build and Go ou faites Cmd + R. Le programme se compile et l’exécution s’affiche finalement dans la console Xcode !
Vous devriez obtenir quelque chose de ce genre :

Citation : Console Xcode

[Session started at 2007-03-31 12:52:23 -0400.]
2007-03-31 12:52:24.929 TutorielOC[730] Hello, World!

TutorielOC has exited with status 0.

Eh oui, le fameux « Hello, World ! », toujours lui !!! 😀 . Jamais on s’en débarrassera ! En fait, c’est juste Xcode qui vous crée un projet viable au début… Il fallait faire un choix sur le premier programme à faire fonctionner… Ils ont choisi le plus connu 😀 .

TutorielOC.m

Mais revenons au projet : cliquez sur le petit bouton avec une icône bleue dessus juste à côté du marteau en haut à gauche de votre fenêtre de projet. Sélectionnez le fichier TutorielOC.m, voilà ce que vous devriez avoir :

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    // insert code here...
    NSLog(@"Hello, World!");
    [pool release];
    return 0;
}

Aïe ! C’est plus vraiment comme en C ! Vous commencez à découvrir l’Objective-C petit à petit…

Commençons par ce qu’on connaît plus ou moins. Déjà, je vous ai parlé de la directive de précompilation #import, bah la voici à nouveau 😉 , important dans notre fichier main <Foundation/Foundation.h> qui est la framework par défaut.
Ensuite, la déclaration de la fonction main() ; pas vraiment différent, là non plus. Il faut que cette fonction retourne un int sinon GCC fait la gueule 😀 , et cette fonction prend deux paramètres qui sont les valeurs inscrites dans la console lorsqu’on appelle le programme… On n’en a pas besoin, là.

Rentrons dans les accolades. On voit le return 0; à la fin, signe que le programme s’est bien exécuté… Et juste au-dessus, un Hello World dans une fonction NSLog().

Donc, cette fonction NSLog est l’équivalent de printf(), à la différence que cette méthode prend une chaîne de caractères Objective-C, et non pas C. C’est l’un des premiers aspects de la différenciation Objective-C / C. En effet, Objective-C a l’avantage de bien différencier la partie objet de la partie procédurale dans les programmes, vous le verrez tout au long de ce tutoriel.

Les strings Objective-C

Les strings Objective-C ne sont que le premier aspect. Donc à chaque fois que je dirais qu’il faut une string, vous devrez entourer vos caractères de ces symboles @ »ma chaine », et à chaque fois que je parlerai de string C, vous utiliserez cette notation : « ma chaine », sans les parenthèses bien sûr.
La string Objective-C n’est pas différente de la string C, vous pouvez faire tout ce que vous pouviez faire avec les chaînes C ; par exemple, si je veux afficher un entier dans ma string, il suffira d’écrire :

NSLog(@"Un entier : %d\n",maVariable);

Un petit symbole de format est ajouté en plus des : %d, %ld, %f, %Lf, %s, %c…, c’est le formateur %@, c’est l’équivalent de %s mais pour les chaînes de caractères Objective-C.
De la même façon que vous pouvez écrire ça :

NSLog(@"Ma chaine C : %s","un exemple de chaine C");

vous pouvez écrire :

NSLog(@"Ma chaine Objective-C : %@",@"un exemple de chaine Objective-C");
L’appel de fonction VS – L’envoi de message

Voilà pour ce qu’on connaît déjà… Maintenant, regardons ce qu’on ne connaît pas encore : la première instruction de main() et l’avant-dernière !
Pour la première, il s’agit de la déclaration d’une NSAutoreleasePool qui sera utilisée pour la gestion de la mémoire (on ne va pas trop s’étendre là-dessus pour l’instant 😀 ) suivie de messages… Il s’agit d’appel de fonction mais pour des méthodes et des objets… Vous pouvez voir que ce n’est pas du tout écrit de la même façon… C’est même très bizarre au premier abord, n’est-ce pas ? 😀
Bah, moi, c’est ce que j’aime en Objective-C : la partie procédurale en C est différenciée de la partie objet. Donc, difficile de se mélanger les pinceaux entre fonctions C et méthode d’un objet !

Voilà tout pour votre premier programme… J’y vais doucement parce qu’après, ça va devenir de plus en plus imposant ; alors, il vaut mieux bien séparer les parties 😀 . Vous pouvez pour l’instant faire tout ce que vous faisiez en C dans le tutoriel de M@teo, mais ça n’a pas grand-intérêt, au mieux vous rafraîchir la mémoire.
Dans le chapitre suivant, nous allons entamer les choses plus sérieuses.




 

Les messages

Voilà : maintenant, on peut passer à la programmation objet… Vous allez enfin comprendre ce que signifie cette fameuse première instruction sur le NSAutoreleasePool. Vous allez découvrir les messages et comment on les écrit, ainsi que la déclaration des objets et leur utilisation. Vous allez aussi découvrir un exemple d’objet avec la classe NSString pour illustrer l’envoi de message.

 

La syntaxe

Voilà comment on écrit un message :

[receveur message];

il ne faut pas oublier le point-virgule « ; » à la fin de chaque instruction comme en C, les messages ne dérogent pas à la règle.
Expliquons ce petit texte. Le receveur du message, c’est tout simplement un objet, on met tout simplement le nom de la variable.

Et le « message » est tout simplement le nom de la méthode et ses arguments.

Dans le code créé par Xcode, nous avons déjà 3 exemples d’envoi de message. Pour commencer, vous voyez qu’on déclare une NSAutoreleasePool nommée *pool, vous remarquez la présence de la petite étoile que je vous interdis d’oublier pour les objets ! 😀
Regardons les crochets les plus à l’intérieur : [NSAutoreleasePool alloc]
Ici, le receveur du message est NSAutoreleasePool… Je vous avais parlé des objet-classes, eh bien en voici un, c’est l’objet-classe de la classe NSAutoreleasepool, on lui envoie le message alloc qui lui demande de créer une instance, c’est-à-dire un objet, de NSAutoreleasePool en lui attribuant de l’espace mémoire.

Ici donc notre méthode alloc renvoie un id, en l’occurrence il s’agit d’une NSAutoreleasePool ; le problème, c’est que cet objet est inutilisable après l’appel d’une méthode alloc, il nécessite une initialisation. Comme alloc renvoie un objet, et qu’on peut mettre n’importe quel objet comme receveur d’un message, il est possible de faire l’allocation et l’initialisation en un seul bloc.
C’est ce que fait la première instruction : on envoie le message init à l’objet retourné par la méthode alloc. init est la méthode d’initialisation par défaut de tous les objets, elle ne prend aucun argument, tout comme alloc et renvoie id, tout comme alloc. À la fin de l’instruction, on a créé un objet NSAutoreleasePool alloué et initialisé, et on a mis l’adresse de cet objet dans notre variable *pool. pool devient donc un objet NSAutoreleasePool.

Si on voulait faire cette instruction en plusieurs instructions différentes, cela ressemblerait à ça :

NSAutoreleasePool *pool;
pool = [NSAutoreleasePool alloc];
[pool init];

Mais pour des raisons de simplification et de réduction du code, on réduit ces 3 instructions en une seule. C’est comme ça que vous créerez un objet généralement, mais il y a d’autres façons.


 

La Classe NSString

De même qu’en C++ avec la classe string, Objective-C possède lui aussi sa propre classe pour gérer les chaînes de caractères. Il s’agit de NSString. Je vous en ai déjà plus ou moins parlé : en fait, quand vous écrivez quelque chose comme @ »ma chaine », vous déclarez un objet de type NSString. Une chaîne de caractères NSString statique pour être précis. Les NSString sont les seuls objets de tout Objective-C dont vous n’aurez pas à gérer la mémoire, alors profitez-en 😀 .

Je vous parle de cette classe pour vous présenter les méthodes incluant des arguments. Et aussi pour vous montrer d’autres façons de créer des objets :magicien: .
Donc, créons une petite NSString à partir d’un format, c’est-à-dire une chaîne de caractères à laquelle on ajoute des variables, des %d et autres… :

int unNombre = 5; // Nombre qu'on affichera dans la chaîne
NSString *stringe; // On déclare notre NSString
stringe = [NSString stringWithFormat:@"\nMon nombre : %d\n",unNombre];
NSLog(stringe); // On affiche la NSString

Regardons le message qu’on envoie à NSString : vous remarquez l’apparition des deux points « : » dans le nom de la méthode. Ces deux points font bel et bien partie du nom de la méthode. Le nom, c’est bien stringWithFormat: et non pas stringWithFormat, la virgule se trouvant à la suite de notre chaîne n’en fait en revanche pas partie. Il s’agit simplement là d’un format qui se construit de la même façon qu’en C : la première partie représente la chaîne à écrire, et les virgules séparent les variables à introduire dans la chaîne.

À présent, parons des arguments. Les arguments à passer dans un message sont toujours précédés de deux points « : ». Si vous avez une méthode qui prend 4 arguments, le nom de cette méthode ressemblera à ça : maMethode::::. Ce n’est pas très explicite, n’est-ce pas ? Voilà une grosse différence avec le C et les autres langages de ce genre… Comme les deux points font partie du nom de la méthode, il est possible d’expliciter chaque argument en écrivant ce qu’il signifie juste avant les deux points, par exemple : afficherLaDate:dansLaFenetre:aLaCouleur:etLaTaille: lorsque vous appellerez une méthode comme celle-ci, vous réécrirez tout en mettant juste après les deux points vos arguments :

[ecran afficherLaDate:maDate dansLaFenetre:maFenetre aLaCouleur: rouge etLaTaille: taille];

Xcode permet notamment d’écrire les noms des méthodes sur plusieurs lignes en superposant les deux points « : » pour améliorer la lisibilité.

Est-ce qu’il est possible de mettre un message en argument ?

Bien sûr, à condition que ce message retourne quelque chose, car comme en C, un message peut retourner void, mais il peut aussi retourner les types primitifs comme int ou float ; là, il faut faire attention à passer les bon types de variables.


 

Les utilisations communes

Allez, revoyons comment on utilise généralement les messages.

Les messages ne retournant rien

C’est ceux qu’on voit généralement, on les met seuls sur une instruction. J’entends par là que les crochets de messages comme eux doivent être les plus à l’extérieur. On utilise ce genre de messages pour demander à un objet de s’afficher sur un écran par exemple, ou alors de modifier une de ses variables d’instance. Les modificateurs sont généralement des méthodes qui ne retournent rien, c’est-à-dire void, exemples :

[text setColor:uneCouleur];
[fichier open];
Les messages retournant « id »

Ces messages sont assez pratiques, ils retournent généralement le receveur du message. C’est un peu bizarre, mais ça permet de faire en une seule instruction des envois de messages en cascades sur le même objet. L’objet visé étant l’objet se trouvant au niveau le plus bas dans les accolades, exemple :

[[[[receveur message1] message2] message3] message4];

Ce genre d’instruction est pratique dans le cas des constructions d’objets.

Les « constructeurs »

J’ai mis « constructeurs » entre guillemets parce qu’ils ne sont pas comme en C++. En Objective-C, on utilise une méthode pour créer un espace mémoire et une méthode pour initialiser cet espace, l’allocation s’appelle sur l’objet classe. Alors qu’en C++, un constructeur s’appelle sur l’objet construit et ne retourne rien. Chez nous, il retourne quelque chose : l’objet construit. Cet objet est de type id, c’est-à-dire que vous pouvez le stocker aussi bien dans une variable de type id que dans une variable du type créé. Exemple :

NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
Les messages retournant un autre objet qu’eux-mêmes

Ces messages-là retournent généralement un type précis, il s’agit généralement d’accesseurs. Ils permettent de récupérer la valeur d’une variable d’instance (on va voir ce que c’est lors de la définition des classes) qu’ils implémentent. On peut là aussi mettre ces messages en receveurs d’autres messages, mais le message ne sera pas reçu par le receveur du premier message. Exemple :

[uneFenetre contentView];

Cette méthode, on la verra en interface graphique ; grosso-modo, elle permet de récupérer le contenu de la fenêtre uneFenetre.

Les messages retournant autre chose que des objets

Il s’agit là aussi généralement d’accesseurs, ou de résultats de calculs, ils retournent des variables primitives comme en C, par exemple des structures, des int, des float, etc. Tout ce qu’une fonction C normale est capable de retourner.

Si c’est pareil, à quoi sert la couche C de l’Objective-C ?

C’est sûr qu’on pourrait se créer une classe qu’on n’instancierait jamais et qui contiendrait des méthodes faisant des calculs simples, genre l’addition de deux structures. Mais on ne fait pas ça. Quand on envoie un message, le runtime doit découvrir le type du receveur, vérifier s’il peut exécuter la méthode et ensuite la lui faire exécuter. Ce sont des opérations très lourdes pour une addition de structures. Donc, on préférera utiliser les méthodes lorsque l’objet et ses variables d’instance sont directement impliquées, et on utilisera les fonctions pour les codes n’impliquant pas les objets. Cela fait partie du dynamisme du C de pouvoir aussi utiliser des fonctions ne passant pas par le runtime et permettant ainsi d’accroître la vitesse d’exécution.

Donc les messages retournant des variables autres que des objets sont généralement des accesseurs, ou alors des calculs impliquant les variables d’instance de l’objet receveur. Exemple :

NSPoint resultat = [transformation transformPoint:unPoint];

Cette méthode, envoyée à un objet de la classe NSAffineTransform, retourne les coordonnées du point unPoint après application de la transformation transformation… On verra encore ça en interface graphique 😀 .

Un message comme argument

Comme je l’ai dit plusieurs fois, il est possible de mettre un message comme receveur d’un autre message. À condition que le message receveur retourne un objet ! Mais il est aussi possible de mettre un message en argument, à condition que celui-ci retourne aussi une valeur, mais là, ça peut être aussi bien un type primitif qu’un objet, il faut tout de même s’assurer que le type retourné corresponde au type de l’argument. Exemple :

[uneImage drawInRect:[unBouton bounds]];

Encore de la programmation graphique. Ici, le message bounds envoyé à un bouton retourne un NSRect qui est une structure représentant la position et la taille du bouton. Ça tombe bien parce que la méthode drawInRect: prend en paramètre un NSRect.

Lorsque le type retourné est un type de base

Dans l’exemple ci-dessus, le type retourné par la méthode bounds est un NSRect qui est une simple structure contenant deux structures définies comme telles :

typedef struct _NSPoint {
        float x;
        float y;
} NSPoint;

typedef struct _NSSize {
        float width;
        float height
} NSSize;

typdedef struct _NSRect {
        NSPoint origin;
        NSSize size;
} NSRect;

Il est possible d’accéder aux valeurs contenues dans la structure directement à partir du message sans passer par une variable intermédiaire de cette façon :

[unBouton bounds].size.height;

Il est aussi possible d’utiliser les opérateurs « + », « -« , etc. pour les types non-construits comme les int, float, double…

Donc vous voyez que les messages sont finalement très flexibles et permettent pas mal de tours de passe-passe réduisant la charge de travail.

Ce qui conclut le chapitre sur les messages. Vous touchez ici du doigt la programmation dynamique à la Objective-C… Nous allons à présent passer aux choses sérieuses en programmant nous-mêmes nos propres classes en les faisant interagir avec les classes fournies par Apple.

Cette partie n’est pour le moment pas terminée.

Ce cours n’est pour le moment pas terminé et ne le sera probablement jamais… La programmation Mac est tellement vaste 😀 , je crois qu’elle aura ma peau !



Write a Comment

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *