[Plan du site]
Vous êtes ici ---
> Le Site du Zéro
> Les tutoriels
> Non-Officiels
> Systèmes d'exploitation
> MacOS
> La programmation Mac > Le langage de programmation : Objective-C > Les messages
> Lecture du tutoriel
Les messages
Vous vous apprêtez à lire un tutoriel rédigé par un membre de ce site. Malgré tout le soin que ce membre a pu apporter au tutoriel, nous ne pouvons pas garantir que les informations contenues sur cette page sont exactes à 100%. Merci de garder cela en tête lorsque vous lirez cette page ;o)
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.
Voilà comment on écrit un message :
Code : Autre
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.
En Objective-C, tous les objets doivent être déclarés comme des pointeurs sur objet ! Il ne faut jamais oublié la petite étoile devant le nom de votre variable. En revanche, lors de l'appel du message on ne met pas cette petite étoile.
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.
Les méthodes, comme les fonctions, peuvent renvoyer quelque chose. En l'occurrence la méthode alloc renvoie un id. id est un pointeur sur objet tout ce qu'il y a de plus banal. Contrairement aux autres types, celui-là ne nécessite pas d'étoile, vous pouvez écrire : id monObjet;, cela créera un pointeur sur un objet quelconque sans spécification, vous pourrez y mettre n'importe quel type d'objet après.
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 :
Code : Autre1
2
3
| 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.
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

.
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... :
Code : Autre1
2
3
4
| 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 |
Vous devez insérer ce code entre la déclaration de la NSAutoreleasePool et l'instruction [pool release];.
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 :
Code : Autre1
| [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é.
Il ne faut pas oublier de mettre un espace entre l'argument et la suite du nom de la méthode, sinon ça ne voudra plus rien dire

. Vous pouvez aussi mettre un espace après les deux points, mais ce n'est pas obligatoire.
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.
En Objective-C, comme en C, il est possible d'avoir des méthodes avec un nombre d'arguments variable. La méthode stringWithFormat: en est une. Seuls les arguments se trouvant en fin de message sont en nombres variables. Vous ne verrez pas de méthode de ce genre uneString:maChaine, maVar couleur:uneCouleur.
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 :
Code : C1
2 | [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 :
Code : Autre1
| [[[[receveur message1] message2] message3] message4]; |
Attention ! Cela peut nuire à la clarté du code.

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 :
Code : Autre1
| 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 :
Code : Autre1
| [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 :
Code : Autre1
| 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 :
Code : Autre1
| [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 :
Code : Autre1
2
3
4
5
6
7
8
9
10
11
12
13
14
| 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 :
Code : C1 | [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.