IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

De Java à Cocoa

Image non disponible


précédentsommaire

IV. Initialiser classes et objets

Nous avons vu comment déclarer une classe et comment préparer l'implémentation des méthodes qui seront associées aux messages acceptés par la classe.

Pour aller plus loin, il faut être capable de construire proprement des instances d'une classe, et si besoin initialiser les variables globales utilisées par une classe.

Cette partie a donc pour but de détailler :

  • l'initialisation d'une classe ;
  • le mécanisme de construction d'un objet ;
  • la fin de vie d'un objet.

IV-A. Initialisation de la classe

Les classes ont parfois besoin d'une phase d'initialisation qui leur soit propre. Ceci est particulièrement vrai si l'on utilise des variables de classe.

Java propose un mécanisme simple pour initialiser les membres d'une classe : l'initialisation au moment de la déclaration :

 
Sélectionnez
class UneClasse {
 // Une constante
 static public final String LABEL = "unLabel";

 // Un objet
 static private java.net.URL baseURL =
   new java.net.URL ( "http://www.google.com/" ) ;
}

Cette méthode, si elle fonctionne très bien pose un problème pour la création d'objets plus complexes. Dans le cas de la construction de la variable baseURL, on se trouve dans un cas où une solution doit être trouvée pour pouvoir gérer les cas d'erreurs.

En effet, la construction de l'URL peut générer une exception qui ne sera pas traitée par la classe, mais qui sera interceptée par le chargeur de classe (ClassLoader). Pour peu que la classe ne soit pas initialisée explicitement, une telle erreur peut être ensuite plus délicate à identifier correctement.

Une bien meilleure solution est proposée en Java : l'initialisation dans le bloc static.

 
Sélectionnez
class UneClasse {
 // Une constante
 static public final String LABEL = "unLabel";

 // Un objet non initialisé
 static private java.net.URL baseURL;

 // Initialisation
 static {
   try {
     baseURL = new java.net.URL ( "http://www.google.com/" ) ;
   }
   catch ( final Throwable t ) {
     // ... Je peux gérer les erreurs
   }
 }
}

Objective-C propose un système tout à fait comparable, mais sans introduire de syntaxe particulière.

Comme dans Objective-C les classes sont des objets comme les autres, l'objet associé à une classe peut être initialisé en implémentant une méthode initialize.

La déclaration se fait dans le fichier d'interface :

 
Sélectionnez
@interface UneClasse : NSObject {
}

+ (void) initialize; // Initialise les membres de classe

@end

L'implémentation est localisée avec celle des autres méthodes :

 
Sélectionnez
static NSURL baseURL;

@implementation UneClasse

+ (void) initialize
{
 baseURL = [NSURL URLWithString: "http://www.google.com/"];
}

@end

Il est important de remarquer que dans un cas comme dans l'autre l'initialisation des membres de classe est appelée pour chaque classe chargée en mémoire. Il ne faut donc pas appeler la méthode initialize de la classe parente.

Les états que peut prendre une classe sont présentés dans le diagramme d'états ci-dessous :

Image non disponible

Pour résumer :

 

Java

Objective-C

Principe

Construction Syntaxique

Méthode de classe

Initialisation

static { ... }

+initialize ;

IV-B. Construction d'instance

En Java comme en Objective-C les instances d'une classe sont construites en deux étapes :

  1. Allocation de la zone mémoire contenant l'objet ;
  2. Initialisation des valeurs des membres de l'objet.

La syntaxe Java est la suivante :

 
Sélectionnez
String chaine = new String("une chaine");

L'opérateur new masque les opérations d'allocation et d'initialisation en une seule étape. C'est la syntaxe de Java qui oblige simplement d'indiquer quel constructeur va être utilisé pour initialiser l'objet. L'opérateur new alloue automatiquement la zone mémoire avant d'appeler le constructeur.

Dans le respect de la philosophie d'Objective-C, le langage n'ajoute aucune structure syntaxique pour cette opération. Les deux opérations sont explicitement visibles dans le code source en deux parties :

  1. L'appel d'allocation, envoyé à la classe ;
  2. L'appel d'initialisation, envoyé à la nouvelle instance.

Les méthodes appelées sont :

  1. +alloc pour allouer la zone mémoire ;
  2. Par convention, l'objet doit répondre à un message init pour s'initialiser. Mais plusieurs variantes sont possibles (6)

La syntaxe normale pour créer une instance est la suivante : (7)

 
Sélectionnez
NSString * chaine = [NSString alloc];
[chaine initWithCString:"une chaine"
 encoding: NSISOLatin1StringEncoding];

Il est bien évident que cette syntaxe est un peu trop longue. La construction est généralement compactée comme suit :

 
Sélectionnez
NSString * chaine =
 [NSString alloc] initWithCString:"une chaine"
   encoding: NSISOLatin1StringEncoding]];

La déclaration des constructeurs se fait dans le fichier d'en-tête .h :

 
Sélectionnez
@interface UneClasse : NSObject {
}

- (id) init;

@end

Pour sa part l'implémentation est logiquement dans le fichier .m :

 
Sélectionnez
@implementation UneClasse

- (id) init
{
 [super init]; // Il faut initialiser l'objet parent!
 // Mes initialisations spécifiques ci-dessous
 // ...
}

@end

La solution proposée par Objective-C a l'avantage de la simplicité syntaxique en limitant les extensions imposées au C. C'est aussi un moyen très simple pour une classe de suivre les créations d'instances puisqu'il est tout à fait possible de surcharger la méthode alloc.

Si la chose est tout à fait possible en Java, elle doit cependant être réalisée dans le constructeur, ce qui peut malheureusement être surchargé. S'ajoute également l'inconvénient de mélanger un code spécifiquement lié au fonctionnement de la classe avec du code spécifique aux instances.

 

Java

Objective-C

Principe

Opérateur et méthode d'instance spéciale

Méthode de classe et méthodes d'instance

Allocation

new UneClasse(...) ;

[UneClasse alloc]

Initialisation

UneClasse()

- init

Initialisation
avec options

UneClasse(p1, p2)

- initFromParam : p1 andParam : p2

Un objet suit les évolutions suivantes :

  1. allocation
  2. initialisation
  3. utilisation
  4. libération

Ce qui est résumé dans le diagramme d'état suivant :

Image non disponible

IV-C. Destruction d'une instance

Lorsqu'un objet devient inutile, il est souhaitable de le supprimer pour récupérer l'espace mémoire qui est lui associé.

En Java la gestion de la mémoire est quasi transparente du fait de la machine virtuelle qui gère tout avec un système de ramasse-miettes (garbage collection).

La situation est très similaire avec Cocoa. Dans sa version actuelle, le ramasse-miettes n'est pas implémenté, mais il le sera dans la prochaine version disponible avec Léopard (Mac OS X.5). C'est donc au développeur de gérer la mémoire dans son application. Bien que simple, cette gestion de la mémoire fera l'objet d'un article spécifique.

Mais même avec un ramasse-miettes Java permet au développeur de faire le ménage dans ses objets. C'est la méthode finalize qui s'en charge :

 
Sélectionnez
protected void finalize() throws Throwable

Cette méthode est appelée par le ramasse-miettes et doit donc se contenter de vider ses collections ou remettre les références vers des objets tiers à null.

 
Sélectionnez
protected void finalize() throws Throwable {
 // Mon ménage ci-dessous
 // ...
 // Le ménage pour la partie de la classe parente.
 super.finalize();
}

C'est exactement le même principe en Objective-C.

Pour faire le ménage derrière elle, une classe doit proposer une implémentation de la méthode dealloc. Et comme en Java, la méthode doit faire suivre l'appel vers sa classe parente.

 
Sélectionnez
- (void) dealloc {
 // Mon ménage ci-dessous
 // ... release des références d'objet
 // ... mise à nil des références facultatives, mais utiles

 // Le ménage pour la partie de la classe parente.
 [super dealloc];
}

Il est important de noter que cette méthode peut ne pas être appelée lorsqu'on quitte une application. Dans ce cas, il est plus efficace de laisser le système d'exploitation libérer la mémoire allouée par l'application en un seul bloc, et c'est ce que fait le runtime Objective-C.


précédentsommaire
La littérature présente souvent cela sous la forme init…, pour indiquer « toutes les méthodes commençant par init ».
Dans cet exemple, on utilise un constructeur avec paramètres.

Licence Creative Commons
Le contenu de cet article est rédigé par Sylvain Gamel et est mis à disposition selon les termes de la Licence Creative Commons Attribution 3.0 non transposé.
Les logos Developpez.com, en-tête, pied de page, css, et look & feel de l'article sont Copyright © 2013 Developpez.com.