Résumé

Face à un C++ puissant, efficace et maîtrisé, Objective-C surprend par sa richesse et sa souplesse. Adressé au développeur confirmé, ce livre dense et érudit guidera les amoureux de la programmation iOS et Mac OS X à travers toutes les subtilités de ce langage.

Objective-C, langage objet indispensable pour développer en natif sous Mac OS X et pour l’iPhone et l’iPad

Avec le succès de l’iPhone et de l’iPad, la maîtrise d’Objective-C, langage natif des systèmes Apple Mac OS X et iOS, devient un passage obligé pour les professionnels de la programmation – alors que l’engouement pour ces systèmes ne l’a introduit que récemment dans la formation classique des développeurs.

Adressé au développeur qui connaît déjà d’autres langages objet, cet ouvrage éclaire toutes les subtilités d’Objective-C en le comparant avec les principaux langages que sont C++, Java, C# : syntaxe et concepts objet (classes, héritage, instanciation), gestion de la mémoire, chaînes de caractères, exceptions, multithreading, concept des propriétés, mécanismes de modifications à l’exécution… sans oublier les récentes évolutions apportées au langage.

Au sommaire

De C#, Java, C++/QT à Objective-C • Du C au modèle objet • Objective-C et ses API (Cocoa…) • Racines non Unix • Bibliothèque runtime • Compilation et linkage • 32/64 bits • Interopérabilité avec d’autres langages • Core Foundation, toll-free bridging et gestion de la mémoire • Robustesse du langage • Syntaxe • Vrais et faux mots-clefs • Syntaxe du C sous-jacent • Commentaires • Code/déclarations • Boucle for • Nouveaux types et valeurs • Organisation du code : fichiers .h, fichiers .m et inclusion • Fonctions et méthodes : une similarité trompeuse • Classes et objets • Classe racine, type id, valeurs nil et Nil • Déclaration • Attributs et méthodes • Déclarations anticipées (forward) : @class, @protocol • Visibilité public, protected, private • Messages et fonctions • Prototype et appel, méthodes d’instance, méthodes de classe • Cibles self et super • Identifiant et signature • Surcharge • Callbacks • Modificateurs de prototype • Messages et transmission • Délégation de message • Forwarding • Manipulations au runtime • Formes d’héritage • Héritage simple et multiple • Downcasting • Méthodes virtuelles • Redéfinition et surcharge • Méthodes virtuelles pures • Extension de classe (sans héritage) : les catégories • Classes abstraites • Protocoles formels et informels • Design-pattern Class-cluster • Instanciation, destruction et copie • Variable objet et pointeur d’objets • Forme de Coplien • Constructeurs, initialisateurs • alloc et init • self = [super init…] • Listes d’initialisation • Constructeur virtuel • Constructeur de classe • Destructeurs • Clonage classique et pseudoclonage • Comparaison d’objets • == et operator<() en C++ • Hash • Gestion de la mémoire • new et delete • Compteur de références (retain, release) • Ramasse-miettes : une courte vie • ARC (Automatic Reference Counting) • Autorelease et bassin d’autorelease • Garbage collector • Finalize • Liens faibles et forts • Singleton • Chaînes de caractères • NSString • Encodages • Chaîne C • Extension de format %@ • Description d’objet • Expressions régulières • Exceptions • @try, @throw, @catch • @finally • Filtres d’exception • Bassin d’autorelease local • Exceptions C++ • Spécificités 64 bits • Multithreading • @synchronized • Volatile • Utilitaires Cocoa et système • API de répartition de charge • Boucle événementielle • Code implicite et propriétés • Key-value coding • Interception • Accès aux propriétés • Différence entre « -> » et « . » • Dynamisme et runtime • RTTI du C++ • Introspection • Typage fort ou faible • Méthode et fonction d’implémentation • Chargement au runtime • Enrichissement conditionnel • Swizzling • Binaire Objective-C et outils de consultation • Bibliothèques de classes standard. • Conteneurs • Gestion mémoire • Itérateurs • Énumération rapide • Foncteurs (objets-fonctions) • Mise en cache d’un appel de méthode • Fonctions de rappels (callbacks) par blocs • Évolutions d’Objective C • Migration vers le 64 bits • Objective-C 2.0 • Objective C++ • Blocks • Closure (fermeture) • Les tagged-pointers • Annexes • Fonctionnalités propres au C++ • Références • Inlining • Templates • Surcharge d’opérateurs • Friends • Méthodes const • Namespace (espaces de noms) • C++/Objective-C.

À qui s’adresse cet ouvrage ?

– Développeurs expérimentés C++, Java, C#, Python et PHP 5 qui veulent développer pour iPhone ou Mac OS X

– Professionnels du développement web et mobile

– Fans d’iPhone qui souhaitent développer pour iPhone et Mac OS X

Biographie auteur

P. Y. Chatelier

Pierre Y. Chatelier développe pour Mac OS X par passion depuis qu’il a découvert cette plate-forme en 2002, et est maintenant l’auteur de plusieurs logiciels pour Mac OS et iPhone. Après avoir fait ses classes d’ingénieur à l’Isima de Clermont-Ferrand et obtenu un doctorat, il occupe maintenant un poste en recherche & développement en Bourgogne et souhaite promouvoir le langage Objective-C, dont les qualités méritent d’être connues des néophytes comme des programmeurs confirmés.

www.editions-eyrolles.com

Objective-C

pour le développeur avancé

Le langage d’iOS 6 et Mac OS X
pour les développeurs C++/Java/C#

2e édition

Pierre Y. Chatelier

...

ÉDITIONS EYROLLES

61, bd Saint-Germain

75240 Paris Cedex 05

www.editions-eyrolles.com

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 l’autorisation de l’Éditeur ou du Centre Français d’exploitation du droit de copie, 20, rue des Grands Augustins, 75006 Paris.
© Groupe Eyrolles, 2013, ISBN : 978-2-212-13686-9

COLLECTION BLANCHE

F. DAOUST, D. HAZAËl-MASSIEUX. – Relever le défi du Web mobile.

Bonnes pratiques de conception et de développement.

N°12828, 2011, 300 pages.

E. SARRION. – jQuery Mobile.

La bibliothèque JavaScript pour le Web mobile.

N°13388, 2012, 610 pages.

J. STARK. – Applications iPhone avec HTML, CSS et JavaScript.

Conversions en natifs avec PhoneGap.

N°12745, 2010, 190 pages.

J.-M. DEFRANCE. –Ajax, jQuery et PHP.

42 ateliers pour concevoir des applications web 2.0.

N°13271, 3e édition, 2011, 482 pages.

R. GOETTER. – CSS avancées. Vers HTML 5 et CSS 3.

N°13405, 2e édition, 2012, 400 pages.

R. RIMELÉ. HTML5. Une référence pour le développeur web.

N°13638, à paraître en 2013, 644 pages.

E. DASPET, C. PIERRE DE GEYER. – PHP 5 avancé.

N°13435, 6e édition, 2012, 900 pages environ.

J. PAULI, G. PLESSIS, C. PIERRE DE GEYER. – Audit et optimisation LAMP.

N°12800, 2012, 300 pages environ.

S. JABER. – Programmation GWT 2.5.

Développer des applications HTML5/JavaScript en Java avec Google Web Toolkit.

N°13478, 2e édition, 2012, 540 pages.

C. PORTENEUVE. – Bien développer pour le Web 2.0.

Bonnes pratiques Ajax.

N°12391, 2e édition, 2008, 674 pages.

COLLECTION ACCÈS LIBRE

A. FAQUE. – Google Android 4 efficace

Utilisation avancée des smartphones Android (Samsung Galaxy, Nexus, HTC…)

N° 13481, 2012, 218 pages.

T. BAILLET. – Créer son propre thème WordPress pour mobile.

N°13441, 2012, 128 pages.

A. BOUCHER. – Ergonomie web illustrée. 60 sites à la loupe.

N°12695, 2010, 302 pages (Design & Interface).

I. CANIVET. – Bien rédiger pour le Web.

Stratégie de contenu pour améliorer son référencement naturel.

N°12883, 2e édition, 2011, 552 pages.

N. CHU. – Réussir un projet de site web.

N°12742, 6e édition, 2010, 256 pages.

H. COCRIAMONT. – Réussir son premier site Joomla! 2.5.

N°13425, 2012, 160 pages.

COLLECTION DESIGN WEB

C. SCHILLINGER. – Intégration web :...

Apolline,

Grâce à toi, ça fait deux.

Avant-propos

Pourquoi ce livre ?

Si vous vous intéressez à la programmation native pour Mac OS X ou pour iPod Touch/iPhone/iPad (iOS), vous n’échapperez pas 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 renforcée de jour en jour.

Ce livre enseigne Objective–C, avec la particularité de comparer en permanence ses fonctionnalités avec celles des autres langages objet principaux (Java, C++, C#). Le but...

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 page suivante, 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.

DYNAMISME ET PERFORMANCES L’un ne chasse pas l’autre

Peut-être voyez-vous déjà dans cette divergence un avantage très net de C++ pour ce qui est des performances. C’est en partie vrai. Les performances d’Objective–C sont toutefois excellentes lorsqu’il est correctement utilisé. Une section y est consacrée dans le chapitre 13 intitulé « Les évolutions d’Objective–C : performances et extensions du langage », en fin d’ouvrage.

Java veut se placer en remplaçant du C++, toujours avec une syntaxe très proche du C mais un meilleur modèle objet, lui aussi inspiré de Smalltalk.

Le langage C#, développé par Microsoft, est un concurrent direct d’Objective–C. Les similitudes sont nombreuses et dénotent en réalité un renouveau du développement objet pour la conception d’applications modernes.

L’Objective–C++ est une fusion des syntaxes de l’Objective–C et du C++. Attention : il ne s’agit pas vraiment d’un langage, mais plutôt d’une fonctionnalité...

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–C 1.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 clefs de la plus visible d’entre elles. La section « Objective–C 2.0 » du chapitre 13 « 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 utilise certaines mauvaises pratiques autorisées par le C. L’Objective–C n’a fait que rajouter des concepts et les mots-clefs les accompagnant. Pour éviter...

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 5, « 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 variables de type objet, seulement des pointeurs 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’objet nul devrait plutôt être mis à nil (null en Java et C#, nullptr en C++/CLI et C++11). Ces valeurs n’ont pas vocation à être interchangeables, mais elles le sont en pratique puisqu’un pointeur d’objet en Objective–C est bien, techniquement, un pointeur vers une struct C (opaque pour le développeur).

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 C++, Java, 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 (ivar) et méthodes, @interface, @implementation

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

Dans les documentations, ces données d’instance sont aussi nommées ivars.

Tableau 3–1 Déclaration de classe

C++
Objective–C

class A

{

double x; //attribut

public: //méthodes

int f(int x);

float g(int x, int y);

static int h(int x);

};

@interface A : NSObject

{

double x; //donnée d’instance

}

//méthodes

-(int) f:(int)x;

-(float) g:(int)x :(int)y;

+(int) h:(int)x;

@end

…

…

int A::f(int x) {…}

float A::g(int x, int y) {…}

int A::h(int x) {…}

@implementation A

-(int) f:(int)x {…}

-(float) g:(int)x :(int)y {…}

+(int) h:(int)x {…}

@end

En C++, attributs et méthodes sont déclarés ensemble au sein de la classe. Pour implémenter les méthodes, dont la syntaxe est similaire au C, on utilise l’opérateur de résolution de portée A::.

En Java et C#, attributs et méthodes sont déclarés ensemble au sein...

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. Plus loin encore, c’est le chapitre 10, « Code implicite et propriétés », qui clora la syntaxe Objective–C 2.0 par la notion, non essentielle mais très pratique, et largement utilisée maintenant, de propriété. Comprendre ces propriétés nécessite en effet une bonne connaissance de la gestion mémoire.

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)

C++
Objective–C

class A //pas de classe racine en C++

{

…

};

@interface A : NSObject

{

…

}

@end

class B : public A

{

…

};

@interface B : A

{

…

}

@end

Héritage interdit ou limité

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

UN ŒIL SUR LA SUITE final et sealed pour les méthodes

final et sealed existent aussi, en Java, C# et C++11, 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, comme en Java, 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++, 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

C++
Objective–C

class A

{

public:

int pub;

protected:

int pro;

private:

int pri;

};

@interface A : NSObject {

@public

int pub;

@protected

int pro;

@private

int pri;

}

@end

class B : public A

{

public:

int pub;

protected:

int pro;

private:

int pri;

};

@interface B : A {

...

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 » 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.

C++
Objective–C

class A
{

…

};

@interface A : NSObject

…

@end

A a(…);//OK, objet créé sur la pile
A* a =…; //OK, création dynamique
A a(…); //impossible !
A* a = …; //OK création dynamique

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 en 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

Dans quasiment tous les autres langages objets (dont C++/Java/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 exact de l’objet lors de l’exécution du code).

Si les données d’instance doivent être initialisées à des valeurs particulières, dépendantes des paramètres de construction, le code correspondant est déporté dans une méthode d’instance, dont le nom commence traditionnellement...

6

Gestion de la mémoire

Ce chapitre est sans doute le plus important de tout l’ouvrage. La gestion de la mémoire en Objective–C est indiscutablement une de ses grandes forces, mais elle est propice aux erreurs ou incompréhensions 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 trois erreurs majeures provoquées par une mauvaise gestion de la mémoire sont les fuites mémoire, les tentatives de libération multiples, et les tentatives d’accès aux ressources déjà libérées. Or, 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 intrinsèques. Pour le débutant, la situation s’est complexifiée avec l’introduction en 2011 du comptage de références automatique, qui masque la gestion mémoire, tout en conservant les principes auparavant explicites, toujours utilisés. Sachant qu’un ramasse-miettes avait été introduit avec Mac OS 10.6 pour être finalement déprécié sous 10.8, sans avoir jamais été porté pour iOS, il est utile de faire le point.

Au final, la gestion de la mémoire en Objective–C peut se faire de trois façons différentes : par le comptage de références explicite, par le comptage de références automatique, et par le ramasse-miettes.

Pour faciliter les explications, ce chapitre est donc organisé de la façon suivante : dans un premier temps, le comptage de références est expliqué, en retardant le plus possible l’introduction du mot-clef autorelease, qui ne peut être utilisé avant d’en avoir pleinement saisi le sens. Puis, le ramasse-miettes est présenté, avec ses principes de liens forts et faibles. Enfin, le comptage de références automatique est détaillé, puisqu’il fait appel à des principes exposés dans les deux parties précédentes.

Des explications sur les écueils les plus courants sont insérées aux endroits pertinents, afin de consolider la compréhension d’une gestion propre et robuste des allocations et libérations.

new et delete

Les mots-clefs classiques new et delete du C++ (seulement new pour Java et 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]. Contrairement à ce que j’ai pu affirmer dans la première édition de cet ouvrage, elle n’est pas déconseillée officiellement et n’est pas un simple héritage du passé maintenu par rétrocompatibilité. En revanche, je maintiens qu’elle n’est pas une riche idée pour au moins deux raisons – la seconde étant 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. On pourrait étendre cette règle aux méthodes commençant par new ; ce serait juste, mais si peu de méthodes de Cocoa sont concernées (deux ou trois) que leur documentation est une référence plus simple.

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

Comme nous l’avons dit, la gestion de la mémoire en Objective–C est l’un des points les plus importants du langage, et l’un de ses points forts. Dans quasi tous les autres langages, dont 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é, mais delete sera effectué sur un seul des pointeurs. La difficulté vient du fait qu’un pointeur n’a aucun moyen de savoir s’il est le dernier à référencer une zone mémoire.

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 si, à un instant, le chien n’a aucune laisse, il 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é, ce qui provoque la destruction effective de l’objet. 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é ». Si plusieurs objets 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). Au final, la mémoire n’est bien allouée qu’une fois et détruite une fois, mais la responsabilité de...

7

Objets libellés (literals) et chaînes de caractères

On appelle literal (libellé en français) une valeur codée explicitement dans un code source. Pour les objets représentant des types de base, comme les nombres ou les chaînes de caractères, une syntaxe particulière est nécessaire à la description d’un tel objet, qui ne sera construit qu’à l’exécution. Ce chapitre présente les objets libellés utilisables en Objective–C, et se poursuit par la présentation de quelques fonctionnalités périphériques courantes avec les chaînes de caractères.

Dans un code C, les caractères "123" permettent de représenter l’entier 123 ; une chaîne de caractères entre guillemets correspond à un tableau de caractères. Cette syntaxe est si naturelle que l’on oublie que c’est le compilateur qui se charge de les interpréter et d’en produire une forme binaire dans le fichier compilé.

Or, dans un langage objet, il est courant d’avoir des équivalents objets aux types de base. NSNumber* permet par exemple d’encapsuler des nombres pour les stocker dans des collections, ou simplement pour les stocker dans le tas et non sur la pile, sans passer par les allocateurs classiques tels malloc() et free(), et bénéficier de la gestion mémoire « objet ». Chaque langage apporte sa syntaxe pour simplifier l’écriture du code correspondant.

Types de base et encapsulation objet

Dans un langage objet « idéal », tout est objet, même un nombre entier. Pour des raisons pratiques et pour les performances, on...

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(…) mais il est redondant avec @catch(id objet). Il...

9

Le multithreading et les outils associés

Le mot-clef @synchronized d’Objective–C est le seul élément du langage spécifique aux threads. Les threads eux-mêmes et les objets verrous reposent quant à eux sur différentes API, que l’on peut survoler rapidement. En revanche, l’utilisation conjointe des blocks du langage C et de l’API Grand Central Dispatch, élève l’Objective–C à un niveau de convivialité inédit pour l’exécution de code asynchrone et la programmation parallèle.

Il est dans le monde du développement une assertion qui n’a jamais été infirmée, c’est que la programmation multithread est extrêmement difficile. Aussi simple que soit la syntaxe pour la mettre en œuvre, c’est un des domaines les plus propices aux bugs. En effet, le non-déterminisme d’ordre d’exécution du code qui en découle rend les tests exhaustifs quasi impossibles à réaliser, laissant toujours la possibilité d’un cas d’erreur non pris en compte. Ce chapitre explique donc d’abord comment, si possible, se passer du multithreading, puis présente les outils standards en Objective–C.

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,...

A

Fonctionnalités propres à C++/Java/C#

Cette annexe cite quelques fonctionnalités d’autres langages objets (C++/Java/C#) qui n’ont pas d’équivalent direct en Objective–C, soit parce qu’elles n’auraient pas de sens ou d’intérêt, soit parce qu’elles en sont simplement absentes. Bien entendu, toutes les fonctionnalités des autres langages ne sont pas citées.

Cas particulier du C++

Dans la plupart des cas, si des fonctionnalités propres au C++ font vraiment défaut pour un cas d’utilisation précis, Objective–C++ peut en autoriser l’usage. Selon le compilateur, tout ou partie du C++11 doit même pouvoir être employée.

Références

Les références (&) ne sont pas présentes en Objective–C. La gestion mémoire par compteur de références et l’autorelease permettent...