Objective-C
pour le développeur avancé

Le langage iPhone/iPad et Mac OS X
pour les développeurs C++/Java/C#

Pierre Y. Chatelier

Groupe Eyrolles

61, bd Saint-Germain

75240 Paris cedex 05

www.editions-eyrolles.com

Aurélie,

Voilà déjà une des trois choses ; nous ferons les deux autres ensemble.

Le code de la propriété intellectuelle du 1er juillet 1992 interdit en effet expressément la photocopie à usage collectif sans autorisation des ayants droit. Or, cette pratique s’est généralisée notamment dans les établissements d’enseignement, provoquant une baisse brutale des achats de livres, au point que la possibilité même pour les auteurs de créer des œuvres nouvelles et de les faire éditer correctement est aujourd’hui menacée. En application de la loi du 11 mars 1957, il est interdit de reproduire intégralement ou partiellement le présent ouvrage, sur quelque support que ce soit, sans autorisation de l’éditeur ou du Centre Français d’Exploitation du Droit de Copie, 20, rue des Grands-Augustins, 75006 Paris.

ISBN 978-2-212127-51-5

© Groupe Eyrolles

Le format ePub a été préparé par Isako www.isako.com à partir de l'édition papier du même ouvrage

Table

Couverture

Titre

Licence

Table

Avant-propos

Pourquoi ce livre ?

À qui s’adresse ce livre ?

De C# à Objective-C

De Java à Objective-C

De C++/QT à Objective-C/Cocoa

Dans quelles conditions a été écrit ce livre ?

Structure de l’ouvrage

Remerciements

1 - Le monde d’Objective-C

Du C au modèle objet : les principaux protagonistes

Objective-C et ses API (Cocoa…) : les racines non Unix de Mac OS X

La bibliothèque standard Objective-C

La documentation officielle d’Objective-C

Compilation d’un programme Objective-C

La bibliothèque runtime d’Objective-C

Compilation et linkage

Code minimal

Différences 32/64 bits

32 et 64 bits côte à côte

Interopérabilité...

Avant-propos

Pourquoi ce livre ?

Si vous vous intéressez à la programmation native pour Mac OS X ou pour iPod Touch/iPhone/iPad (iPhone OS), vous ne pourrez pas échapper au langage Objective-C. Or, ce langage, en apparence né avec Mac OS X en 2001, semble un peu surgi de nulle part. Limité à l’environnement Apple, il ne fait pas partie des formations classiques des développeurs.

Moi-même, il m’avait semblé être au premier abord un obstacle plutôt qu’un tremplin à la programmation pour Mac OS X : il était si peu répandu que je ne comprenais pas son intérêt face à un C++ puissant, efficace et maîtrisé. J’ai cependant découvert qu’il s’agissait au contraire d’un choix particulièrement heureux : Objective-C, par sa richesse et souplesse, est le langage le plus agréable à programmer que je connaisse. De plus, le succès de l’iPhone l’entraînant dans son sillage, la maîtrise d’Objective-C est on ne peut plus d’actualité, et sa pertinence, loin de décroître, est...

1

Le monde d’Objective-C

Vous pensiez peut-être couper à l’inévitable historique, mais celui d’Objective-C est intéressant pour justifier le choix de ce langage par Apple. Il permet aussi d’éclairer sur ce que n’est pas Objective-C, afin d’éviter les confusions. Quelques points techniques relatifs à la compilation sont abordés.

Ce chapitre a pour but de présenter Objective-C et ses satellites, pour définir clairement les rôles. Entre langage, normes, API, extensions et évolutions, quelques précisions sont nécessaires. Les détails techniques avancés sont toutefois reportés tout au long de cet ouvrage, pour ne pas complexifier inutilement cette présentation. Les évolutions du langage sont également récapitulées dans le dernier chapitre.

Du C au modèle objet : les principaux protagonistes

Il est assez difficile de dater précisément les naissances des langages de programmation. Selon qu’on considère leurs balbutiements, leur développement, leur annonce officielle ou leur standardisation, la marge est importante. Malgré tout, un historique sommaire, présenté dans la figure ci-dessous, permet de situer Objective-C vis-à-vis de ses ancêtres et de ses « concurrents ».

Smalltalk-80 est un des tout premiers langages réellement objet, qui a donc fortement influencé les développements ultérieurs de ce concept. Le C++ et l’Objective-C sont deux branches différentes visant à créer un langage objet basé sur le C.

Figure 1–1 - Historique sommaire de Smalltalk, Java, C, C#, C++ et Objective-C

Tandis que C++ se tourne vers une forme de programmation très statique minimisant le « coût » du modèle objet pour les performances d’exécution, Objective-C choisit la voie du dynamisme de Smalltalk, dont il emprunte aussi une partie de la syntaxe.

...

2

Généralités sur la syntaxe

Distinguer les mots-clefs du langage et identifier ce qui fait partie de son standard est nécessaire pour commencer à lire des codes d’exemple. Ce chapitre permet de faire la part des choses entre le vocabulaire imposé et celui des API ou du programmeur.

Ce chapitre est un premier contact avec la syntaxe d’Objective-C1.0. Seules les méthodes, qui nécessitent des explications assez substantielles, sont reportées dans une section « Méthodes, messages et fonctions » du chapitre suivant : « Classes et objets ».

Les nouveautés apportées par Objective C 2.0 n’ont que peu enrichi la syntaxe, et elles sont citées au fur et à mesure de l’ouvrage. La section traitant des propriétés dans le chapitre 10 « Code implicite et propriétés » donne les clés de la plus visible d’entre elles. La section «Objective-C2.0» du chapitre13 «Les évolutions d’Objective-C : performances et extensions du langage » récapitule toutes ces nouveautés mais ne rappelle pas les détails techniques.

Les mots-clefs d’Objective-C

Les vrais mots-clefs

Objective-C est un surensemble du langage C. Comme en C++, un programme correctement écrit en C devrait être compilable en Objective-C, sauf s’il...

3

Classes et objets

Ce chapitre se focalise sur la syntaxe des classes, objets et méthodes, cette syntaxe étant parfois assez particulière à cause de l’originalité, un peu inattendue, des mécanismes sous-jacents. Les fonctionnalités des classes, comme l’héritage, sont traitées dans les chapitres suivants.

Objective-C est un langage objet et il en respecte mieux les paradigmes que C++, lequel présente des lacunes vis-à-vis du modèle idéal. Par exemple, en Objective-C, les classes sont elles-mêmes des objets manipulables dynamiquement.

Cette approche différente du modèle objet a évidemment des incidences sur les fonctionnalités de ces classes. Après la syntaxe de déclaration et définition, seule l’encapsulation est abordée dans ce chapitre. Les détails sur l’héritage et le polymorphisme sont regroupés dans celui qui lui succède.

Classe racine, type id, valeurs nil et Nil

Dans un langage objet, on crée généralement un diagramme de classes pour chaque programme. Une des particularités d’Objective-C par rapport à C++ est l’existence d’une classe racine (root) dont devrait hériter toute nouvelle classe. En Cocoa, il s’agit de NSObject, qui fournit un grand nombre de services et assure la cohérence du système d’exécution d’Objective-C. Mais la notion de classe racine n’est pas une spécificité d’Objective-C, c’est une particularité du modèle objet en général ; Smalltalk, Java, C#, utilisent aussi une classe racine. Le C++ quant à lui ne l’impose pas.

En toute rigueur, tout objet devrait donc être au moins de type NSObject et tout pointeur vers un objet pourrait être déclaré comme un NSObject*. Cependant, ce n’est pas absolument obligatoire : il existe le type id, court et pratique pour exprimer un pointeur vers un objet quelconque, et qui implique une vérification dynamique des types, et non plus statique. Cela peut être utile comme outil de typage faible sur des méthodes génériques : par exemple la classe « tableau » de Cocoa, NSArray, travaille avec des objets a priori inconnus, déclarés de type id.

UN ŒIL SUR LA SUITE - Allocation uniquement dynamique des objets

Vous apprendrez au chapitre « Instanciation, destruction et copie » qu'en Objective-C, comme en Java, aucun objet ne peut être créé sur la pile, ils ne peuvent être alloués que dynamiquement sur le tas. Il n'existe donc pas de variable de type objet, ce ne ne peut être qu'un pointeur d'objets. Il n'existe pas non plus de classe « spéciale » comme la struct de C# (value class en C++/CLI) adaptée à la pile (même si la struct standard du C reste disponible).

En particulier, id ne nécessite pas de cast (transtypage) et ne génère pas de warnings à la compilation.

Notez aussi que si un pointeur C normal peut être mis à NULL, un pointeur d’objets nul devrait plutôt être mis à nil (null en Java et C#, nullptr en C++/CLI). Ces valeurs n’ont pas vocation d’être interchangeables, mais elles le sont en pratique, puisqu’un pointeur d’objets en Objective-C est un pointeur vers une simple struct C.

Il est plus rigoureux et plus lisible de préférer nil. Les classes étant aussi des objets (instances de métaclasses), il existe des pointeurs de classes, pour lesquels la valeur « vide » est Nil (avec une majuscule).

En pratique, nil, Nil et NULL sont bien égaux à 0, il n’est pas problématique de les confondre. Même certains codes d’exemple d’Apple utilisent parfois NULL pour des pointeurs d’objets.

Les tests avec l’opérateur ! sont corrects pour nil et Nil

id objet = …
if (objet) … //(objet != nil)
if (!objet)… //(objet == nil)

Déclaration des classes

Il est difficile de montrer par un seul exemple toutes les différences existantes entre le C++ et l’Objective-C pour la déclaration des classes et l’implémentation des méthodes. En effet, syntaxe et concepts s’entremêlent et nécessitent des explications. Les différences sont donc exposées en plusieurs étapes très ciblées.

Attributs et méthodes

En Objective-C, les attributs sont appelés données d’instance, et les fonctions membres des méthodes.

Tableau 3–1 - Déclaration de classe

En C++, attributs et méthodes sont déclarés ensemble au sein de la classe. Pour implémenter...

4

Les formes d’héritage

L’héritage est plus simple en Objective-C qu’en C++, mais il est aussi moins riche. En contrepartie, il est épaulé par des concepts inconnus du C++ : les protocoles et les catégories. Java et C# peuvent les proposer sous d’autres noms.

L’héritage en C++ est extrêmement puissant, mais il le paye par une certaine complexité technique. À l’image des autres langages objet, Objective-C repose uniquement sur l’héritage simple (on n’hérite que d’une classe), complété par d’autres concepts pour reproduire notamment la notion de classe abstraite ou interface en Java et C#.

Ce chapitre est le dernier à présenter les syntaxes des concepts objets. Dès le chapitre suivant, c’est l’instanciation et les problématiques induites qui sont détaillées, suivies du chapitre 6 sur la gestion mémoire.

Le chapitre 10 « Code implicite et propriétés » qui se trouve encore plus loin clora quant à lui la syntaxe Objective-C 2.0 par la notion, non essentielle, de propriété.

Héritage simple

Objective-C implémente bien sûr la notion d’héritage. On le rencontre très tôt puisque toute classe doit au moins hériter de la classe racine. De façon très similaire au C++, il suffit de spécifier le nom de la classe mère dans le bloc @interface.

Tableau 4–1 - Syntaxe de l’héritage (simple)

Héritage interdit ou limité

En Java et en C#, le mot-clef final (Java) ou sealed (C#) peut s’appliquer aux classes pour interdire d’en hériter. En C++ comme en Objective-C, il n’y a pas de telle limitation.

UN ŒIL SUR LA SUITE - final et sealed pour les méthodes en Java et C#

final et sealed existent aussi, en Java et C#, pour empêcher la redéfinition des méthodes. Ils sont donc également été cités dans la section « Méthodes virtuelles » un peu plus loin dans ce chapitre. Là encore, il n’en existe pas d’équivalent en Objective-C.

En outre, l’héritage ne peut être que public en Objective-C ; on ne peut pas lui spécifier de visibilité particulière comme le C++ qui autorise public, protected et private avec des conséquences sur la visibilité des membres hérités.

Membres homonymes des sous-classes

En C++ comme en Java et C#, une sous-classe peut contenir des données membres de même nom que d’autres données membres de la classe mère. Une résolution de portée explicite peut permettre de lever les ambiguïtés.

En Objective-C, une telle pratique est interdite, quelle que soit la visibilité (public, protected, private) des membres.

Tableau 4–2 - Membres homonymes

AUTRES LANGAGES - Membres homonymes en Java et C#

Java se comporte sur ce sujet comme le C++. C# autorise la pratique, mais le compilateur émet des avertissements tant que cette surcharge de...

5

Instanciation, destruction et copie

Avant même de s’intéresser au cycle de vie d’un objet, la responsabilité de le maintenir dans un état cohérent incombe à celui qui le manipule. À partir du moment où un objet est instancié, il est nécessaire de maîtriser les opérations donnant un sens à son existence, et notamment d’en connaître les pièges.

Maîtriser l’instanciation des classes soulève au moins deux interrogations pour le développeur avisé : comment est implémentée la notion de constructeur/destructeur/ opérateur de copie, et comment est gérée la mémoire ?

Ce chapitre répond à la première question en détaillant les contraintes, les bonnes pratiques, les points d’entrée de la personnalisation, et les erreurs à ne pas faire. Mais bien que l’instanciation fasse déjà appel à la gestion de la mémoire, cette dernière est trop particulière pour ne pas être traitée à part. Elle est entièrement expliquée dans le chapitre suivant.

Variable objet et pointeur d’objets

En C et en C++, les variables sont dites « automatiques » (auto) par défaut : à moins d’être déclarées static, elles n’existent que dans leur bloc de définition. Seule la mémoire allouée dynamiquement persiste jusqu’au free() ou au delete adapté. Les objets n’échappent pas à cette règle en C++.

Cependant, en Objective-C, tout objet est créé dynamiquement. Cet état de fait est somme toute logique, le C++ étant un langage typé statique, et l’Objective-C étant dynamique. Ce dynamisme serait entravé si les objets n’étaient pas créés lors de l’exécution du programme. Consultez le chapitre 6 « Gestion de la mémoire et ramasse-miettes » pour des explications plus poussées sur la façon de maintenir ou de libérer les objets.

En conséquence, il n’y a pas de variable de type objet en Objective-C, il ne peut y avoir que des pointeurs d’objets. Il n’y a donc pas non plus d’objet qui soit construit ou détruit automatiquement, de copie implicite, ou d’optimisation par passage de référence. Les difficultés sont ailleurs.

Tableau 5–1 - En Objective-C aucune variable objet ne peut exister, ce ne sont que des pointeurs d’objets.

AUTRES LANGAGES - Pointeurs d’objets

En Java, les objets sont eux aussi alloués dynamiquement. En C# c’est aussi le cas, mais il existe une variante entre les ref class et les value class, ces dernières pouvant être traitées comme des objets de la pile. Mais il s’agit bien d’un type différent.

POUR ALLER PLUS LOIN - Créer malgré tout des objets Objective-C sur la pile

Un objet Objective-C est un réalité une structure C dotée de nombreux attributs (données, liste de méthodes, caches, autres données internes…). C’est à travers la bibliothèque runtime d’Objective-C que de telles structures sont créées et manipulées simplement.

Sachant cela, le développeur bien informé peut tout à fait créer une telle structure lui-même, et donc la créer sur la pile. Ce n’est pas trivial, mais c’est théoriquement possible. Un article mentionne cette idée à l’adresse suivante :

> http://www.mikeash.com/pyblog/friday-qa-2010-01-15-stack-and-heap-objects-in-objective-c.html.

Forme de Coplien

Peut-être connaissez-vous la dénomination « forme de Coplien » qui regroupe les bonnes pratiques d’implémentation robuste d’une classe C++. Il s’agit du constructeur par défaut, du constructeur par recopie, du destructeur et de l’opérateur d’affectation. La raison d’être de cette liste est la capacité de C++ à construire et à affecter des objets en privilégiant le concis à l’explicite. Il faut prendre garde à prévoir ces actions sur les objets, sous peine de les placer dans des états incohérents vis-à-vis de leurs données membres.

class A {…};
A objet1; //Appelle le constructeur par défaut
A objet2 = objet1; //Appelle le constructeur par copie
objet1 = objet2;   //Appelle l’opérateur d’affectation

En Objective-C, on ne fait pas mention d’une telle forme, car elle n’a plus vraiment de sens. Il n’y a pas de notion de constructeur par défaut, ni de constructeur par copie, puisque les instanciations sont explicites. L’opérateur d’affectation est remplacé par le clonage, lequel est lui aussi explicite.

Ces notions sont présentées dans les sections suivantes.

Constructeurs, initialisateurs

Distinction entre allocation et initialisation

En C++, l’allocation et l’initialisation d’un objet sont confondues dans l’appel au constructeur. En Objective-C, ce sont deux méthodes différentes.

L’allocation est assurée par la méthode de classe alloc, qui a également pour effet d’initialiser toutes les données d’instance à 0 (exceptée une donnée membre de NSObject : le pointeur isa (est-un), qui stocke le type...

6

Gestion de la mémoire

et ramasse-miettes

La gestion de la mémoire en Objective-C est indiscutablement une de ses grandes forces, mais elle est propice aux erreurs de la part des développeurs peu familiers du comptage de références. Ce chapitre a pour but de dissiper l’apparente complexité de cette gestion qui n’est pourtant basée que sur des règles à la fois simples techniquement et parfaitement rigoureuses.

Les deux erreurs majeures provoquées par une mauvaise gestion de la mémoire sont les fuites mémoire et les tentatives de libération multiples. En interdisant les objets automatiques créés et détruits sur la pile d’appel, Objective-C reporte sur le développeur la responsabilité de tous les objets.

Contrairement au Java et au C#, qui ne disposent que d’un ramasse-miettes pour se prémunir des erreurs, Objective-C offre une solution très élégante grâce à une gestion déterministe et rigoureuse de la mémoire par comptage de références. Les difficultés rencontrées par les développeurs sont plutôt liées à l’atypie de cette technique qu’à des lacunes en son sein. Nous avons scindé les explications associées en deux parties : sans, puis avec autorelease, lequel ne peut être utilisé sans en avoir pleinement saisi le sens.

Un ramasse-miettes a effectivement été introduit en Objective-C 2.0, mais il reste optionnel et laisse disponible l’approche initiale par comptage de références. Il n’y a que peu d’explications à donner sur son utilisation, il n’est donc présenté que brièvement par la suite.

Enfin, un résumé des méthodes classiques contenant les écueils les plus courants permet de consolider sa compréhension d’une gestion propre et robuste des allocations et libérations.

new et delete

Les mots-clefs new et delete du C++ n’existent pas en Objective-C. Ils sont respectivement remplacés par un appel à alloc+init (voir le chapitre 5 « Instanciation, destruction et copie ») et à release (détaillé un peu plus loin).

FAUX AMI - La méthode new

La classe NSObject dispose bien d’une méthode new. Cette dernière n’est qu’un simple raccourci pour [[… alloc] init]. Elle existe encore pour la rétro-compatibilité, mais elle est fortement déconseillée pour au moins deux raisons. La seconde raison est compréhensible après avoir lu le présent chapitre.

Si l’initialisateur désigné de la classe n’est pas init, alors new est incorrecte.

Elle n’est pas conforme à l’usage voulant que les seules méthodes incrémentant le compteur de références soient init…, retain et copy.

Compteur de références (retain, release)

La gestion mémoire en Objective-C est l’un des points les plus importants du langage, et l’un de ses points forts. En langage C ou C++, une zone mémoire est allouée une fois et détruite une fois. On peut y faire de multiples références en utilisant le nombre de pointeurs approprié. Sur un seul des pointeurs sera effectué delete.

L’Objective-C implémente quant à lui un système de comptage de références. L’objet est conscient du nombre de liens dirigés vers lui. On peut expliquer ce principe par la métaphore du chien et des laisses (exemple directement tiré du très bon ouvrage Cocoa Programming for Mac OS X). Si l’objet est un chien, chacun peut réclamer une laisse pour le maintenir en place. Si quelqu’un se désintéresse du chien, il peut lâcher la laisse. Tant que le chien a au moins une laisse, il est contraint de rester en place. Mais dès que le nombre de laisses tombe à 0, le chien est… libéré !

Plus techniquement, le compteur de références d’un objet nouvellement créé est fixé à 1. Si une portion du code nécessite de référencer cet objet durablement, elle lui envoie un message retain, qui incrémente de 1 le compteur. Si une portion de code référençant l’objet n’en a plus l’utilité, elle lui envoie un message release, qui décrémente le compteur de 1.

Un objet peut recevoir un nombre quelconque de retain et de release, tant que le compteur de références a une valeur strictement positive. En effet dès qu’il tombe à 0, le destructeur dealloc est automatiquement appelé. Envoyer de nouveau release à l’adresse, devenue invalide, de l’objet provoque une faute mémoire.

De cette manière, le développeur n’a plus à se soucier de centraliser la destruction auprès d’un « propriétaire privilégié ». Des objets qui en référencent un même autre, c’est le dernier à relâcher le lien qui provoque sa destruction. Partager une référence à un objet devient beaucoup plus aisé, moins propice aux fuites mémoire ou aux destructions multiples (et donc invalides).

Notez que cette technique n’est pas équivalente aux auto_ptr de la STL du...

7

Les chaînes de caractères

en Objective-C

La création uniquement dynamique des objets Objective-C devrait rendre impossible l’utilisation de chaînes de caractères codées en dur. Heureusement, il existe bien une notion d’objet « chaîne constante ». Ce chapitre fort succinct en profite aussi pour présenter quelques fonctionnalités périphériques courantes avec les chaînes de caractères.

En langage C, les chaînes de caractères sont des tableaux de char, ou des pointeurs char*. La gestion de ces chaînes est difficile et source de bien des erreurs. La classe string de C++ est un véritable soulagement. En Cocoa, il y a la classe NSString. Mais en Objective-C, il a été expliqué dans le chapitre 5 «...

8

Les exceptions

La gestion des exceptions en Objective-C est un peu plus riche que celle du C++ par l’adjonction de @finally, mais elle est très semblable à celle que l’on peut trouver en Java et C#. L’intérêt de ce chapitre est plutôt de présenter quelques points techniques supplémentaires.

Dans la gestion des exceptions proposée par Objective-C, quatre caractéristiques sont à signaler.

Les mots-clefs sont @throw, @try, @catch, @finally.

On ne peut lancer que des objets, mais sans nécessiter de protocole du style throwable comme en Java.

Le catch(...) (avec des points de suspension) existe sous forme de @catch(...) ...

9

Le multithreading

et les outils associés

Ce chapitre très court introduit le @synchronized d’Objective-C, seul élément du langage relatif aux threads. À l’inverse, les threads eux-mêmes et les objets verrous reposent sur différentes API.

Si Cocoa propose ses propres classes pour gérer les threads (NSThread, NSLock…), rien n’empêche un programme Objective-C d’utiliser les API POSIX classiques (pthread, mutex, cond…). Les problèmes restent identiques : il faut garantir que certaines portions de code, exécutées simultanément, ne donnent pas des résultats imprévisibles en opérant sur le même espace mémoire.

@synchronized

Le support des threads par le langage Objective-C ne repose que sur un mot-clef, @synchronized, équivalent au mot-clef Java du même nom. Le reste...

10

Code implicite et propriétés

Pour n’importe quel développeur, écrire moins de code est toujours un avantage, pour la maintenance comme pour le risque de défaillances. Les «propriétés» ajoutées à Objective-C 2.0 font partie de cet objectif. Afin de bien en saisir l’origine, le Key-value coding est présenté en premier lieu.

Dans cette partie sont regroupées deux fonctionnalités dont la particularité est de permettre un allègement du code. Leurs vocations sont différentes : le Key-value coding peut résoudre un appel de méthode indirect en utilisant la première implémentation valide trouvée, tandis que les « propriétés » permettent de faire générer certaines méthodes rébarbatives au compilateur.

Le Key-value coding est en réalité un service rendu par Cocoa, tandis que la notion de propriété relève du langage lui-même, et a été ajoutée à Objective-C 2.0.

Ainsi, par une utilisation judicieuse des API, et par l’adjonction du support de certaines fonctionnalités par le langage, Apple a mis en place au sein de Cocoa diverses briques fort pratiques pour des techniques de plus haut niveau, comme les bindings. Ceux-ci ne sont cependant pas détaillés dans cet ouvrage, où ils n’auraient pas leur place.

La syntaxe des propriétés aurait pu, quant à elle, être présentée dans le chapitre 3 « Classes et objets », mais cela aurait inutilement complexifié la présentation déjà relativement riche de la syntaxe Objective-C. De plus, les propriétés ne sont pas une fonctionnalité essentielle, mais un raccourci pratique.

Key-value coding

Principe

Le Key-value coding (abrégé en « KVC ») est le nom que l’on donne à la pratique consistant à obtenir ou définir la valeur d’une donnée membre d’objet en y faisant référence par son nom. Cette pratique est similaire à l’utilisation d’un tableau associatif (NSDictionary...

11

Le dynamisme et

les manipulations du runtime

L’introspection, aussi appelée réflexion, permet d’obtenir pendant l’exécution du programme des informations sur les types. En allant plus loin, on peut même modifier ou ajouter des types. Un langage statique et fortement typé comme le C++ ne fait pas usage de telles pratiques, contrairement aux autres langages objet, plus souples. Ils ne font d’ailleurs pas qu’y trouver une utilité : ils en tirent un réel bénéfice dont nous présentons dans ce chapitres des exemples assez parlants.

De l’utilité du dynamisme

Dans un langage objet, l’introspection est une nécessité. Par essence, le principe d’héritage implique que l’on puisse qualifier un objet par son type exact et celui de toutes ses classes mères ; l’identité d’un objet peut donc n’être déterminée qu’en testant s’il est une dérivation de son type apparent, ce qui n’est faisable qu’à l’exécution du code.

Pour aller plus loin, on peut vouloir construire un objet dont le type ne serait connu qu’à l’exécution, voire tester la présence de méthodes, ou même enrichir une classe existante en fonction du contexte d’utilisation.

À travers sa bibliothèque runtime, Objective-C dispose d’une couche d’abstraction très modulaire. Un objet dispose ainsi d’une liste de ses données et méthodes, qu’il est donc possible, en passant par les API adaptées, de manipuler relativement aisément.

USAGE PRATIQUE - Cas courant d’utilisation de l’introspection et des types dynamiques

Interroger des objets de type a priori inconnu est courant dans un processus de délégation, par exemple lorsqu’un composant relativement générique d’interface doit afficher des objets spécifiques fournis par un modèle.

Instancier une classe à partir de son nom sous forme de chaîne de caractères a un sens dans le domaine de la sérialisation (enregistrement persistant, par exemple dans un fichier), mais n’est que difficilement réalisable en C++, où l’on passe par des identifiants et des méthodes « usines ».

Les RTTI du C++ (Run-Time Type Information)

En C++, les classes sont très semblables à des struct, et les données d’instance sont finalement caractérisées par un...

12

La STL et Cocoa

Difficile d’apprendre un langage sans tenir compte de sa bibliothèque standard. Nous ne voulons ni ne pouvons présenter l’intégralité de Cocoa dans ce livre, mais quelques exemples sur les collections d’objets sont utiles pour comprendre comment peut s’articuler Objective-C avec ce genre de code.

La bibliothèque standard du C++ est une de ses grandes forces. Même si elle a quelques lacunes, notamment dans les foncteurs (lacunes souvent comblées dans d’autres implémentations comme la STL SGI ou Boost), elle est malgré tout très riche et vite indispensable. Ce n’est pas à proprement parler une partie du langage, puisqu’elle est construite dessus sans faire partie de sa grammaire, mais on a tôt fait d’en chercher un équivalent dans tout nouveau langage étudié. En l’occurrence, en Objective-C, il faut bien sûr se tourner vers Cocoa pour trouver conteneurs, itérateurs et autres codes fournis clé en main. De même, Java et C# sont tous deux liés à des bibliothèques de base dont on ne saurait se passer.

La manipulation des collections d’objets (ou conteneurs) est un élément essentiel de ces bibliothèques, souvent très riche dans l’exploitation des concepts du langage.

Conteneurs

Les conteneurs standards

Bien sûr,...

13

Les évolutions d’Objective-C :

performances et

extensions du langage

Le langage Objective-C est ancien, mais sa reprise en main par Apple l’a amené à un niveau de modernité tout à fait satisfaisant. On constate avec bonheur qu’il est maintenu à ce niveau par des évolutions simples et discrètes simplifiant la vie des développeurs.

Ce dernier chapitre présente quelques détails d’implémentation, et récapitule les évolutions que sont Objective-C 2.0 et Objective-C++, déjà traitées au long de l’ouvrage.

La notion de block, qui est une évolution du langage C adoptée dans les API d’Apple, trouve aussi sa place ici.

Évolution des performances d’Objective-C

Les évolutions du langage Objective-C ne portent pas forcément sur sa syntaxe seule. Par exemple, le processeur, le système d’exploitation, ou l’interopérabilité même du langage peuvent ouvrir la voie à des améliorations de façon totalement transparente pour l’utilisateur.

Appel des méthodes

Le dynamisme d’Objective-C repose sur les mécanismes de son runtime, qui, soumis à un bon niveau d’abstraction, semble ne pas pouvoir être efficace.

L’appel d’une méthode en C++ passe généralement par l’indirection de la table des méthodes virtuelles (TVM),...