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.