En Objective-C, tout est objet tout comme en Smalltalk dont il s'inspire fortement. C'est donc un langage fortement orienté objet. L'héritage simple induit un arbre d'héritage avec une racine : la classe NSObject, dans le cas de Cocoa/NeXTStep, ou Object dans le cas de GNUstep. C'est à partir d'elle que vont dériver toutes les classes. Par exemple, un objet de classe NSString, ou NSArray, dérive de la classe NSObject (indirectement). NSMutableArray dérive de la classe NSArray, qui est donc sa superclasse. D'après certains de ses utilisateurs, c'est là que la puissance d'Objective-C apparaît : au contraire de C++ ou autres langages ne l'incluant pas, le typage faible permet de manipuler plus simplement des données.
En effet, plutôt que de devoir manipuler de nombreux types, il n'y en a que quelques uns, par exemple dans le cas de Cocoa :
- id qui est un pointeur vers n'importe quel type d'objet.
- BOOL qui est identique à char de C, mais utilisé comme une valeur booléenne. 0 est considéré comme faux, et tout ce qui n'est pas 0, est considéré comme vrai (A, a, 1, 2, etc.)
- YES qui vaut 1.
- NO qui vaut 0.
- IBOutlet est un macro qui ne sert qu'à indiquer à Interface Builder qu'il est en face d'un « Outlet ».
- IBAction qui équivaut à void, et qui indique à Interface Builder qu'il est une action (ou méthode de classe) devant être appelée par exemple par un bouton pressé.
- nil qui est identique à NULL. Il sera préféré à celui-ci, car un « message » envoyé à nil, ne crée pas une exception, et donc un plantage du programme, mais ne renvoie « rien », donc « nil ».
De plus toutes les variables d'instances de classe sont par défaut protégées, et les méthodes de classe publiques. Il permet donc aux programmeurs d'avoir une programmation plus rigoureuse, tout en étant plus rapide, et en respectant les concepts de la POO.
Un autre aspect est celui du modèle de conception KVC (Key-Value Coding), lui aussi inspiré de Smalltalk qui définit l'accès à une variable par son nom. Par exemple, dans le cas d'une classe Personne, avec une variable d'instance Surname, de type NSString :
@interface Personne : NSObject { // variables d'instance NSString *surname; } // methodes @property (copy) NSString *surname; @end
Objective-c permet la création rapide d'objet NSString grâce au @"", de la même manière que le C avec les chaines de caractères "". Les NSString sont encodés en unicode, c'est à dire que contrairement au C, les caractères ne sont pas limités aux codes ASCII. Nous pouvons donc fixer la valeur de la variable surname d'une instance de Personne de cette façon :
// On crée un pointeur vers un objet de la classe Personne, qu'on a d'abord alloué dans l'espace mémoire, et initialisé. Personne *exemple = [[Personne alloc] init]; //On envoie un message au pointeur exemple qui va le rediriger vers l'objet vers lequel il pointe, pour fixer la valeur de la clef surname. [exemple setValue:@"Paul" forKey:@"surname"];
Messages
En objective-c, tout appel de méthode d'une classe est un passage de message. Pour appeler un message sur un objet, on place entre crochet l'objet puis le message. On peut chainer les appels très facilement. Ainsi dans l'exemple, "methode" renvoie un objet et sur cet objet on appel "methode2".
// passage de message [objet methode:argument]; // chainage de message [[objet methode:argument] metode2:argument2];
La syntaxe des méthodes un peu déroutante au première abord a été pensée pour qu'elle ressemble plus au langage humain. Chaque argument est séparé par ":" ainsi qu'un commentaire.
// déclaration d'une méthode - (void)eatWith:(NSString*)name andSpeakAbout:(NSString*)subject; // exemple d'appel de la méthode sur un objet [Paul eatWith:Pauline andSpeakAbout:Subject];
Chaque instance d'objet possède un pointeur isa, c'est à dire un pointeur sur un objet metaclass qui décrit les méthodes accessibles par l'objet. Une class ayant un parent, les metaclass représentent un arbre avec toute la hiérarchie des classes actuellement en mémoire. Ainsi lorsqu'on tente de passer un message, l'environnement Objective-C récupère le pointeur isa de l'objet, ensuite parcourt l'arborescence de la metaclass pour obtenir le pointeur de la méthode appelée. Ce mécanisme est évidemment couteux mais un dispositif de mise en cache lors du premier appel le rend très performant.
// définition du type id qui contient le pointeur isa typedef struct objc_class *Class; typedef struct objc_object { Class isa; } *id;