private

Méthodes privées en Objective-C

Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license can be found at: http://www.gnu.org/copyleft/fdl.html

Introduction

En Objective-C, contrairement à de nombreux autres langages orientés objet, le concept de méthode privée n’existe pas en tant que tel.
Dans la déclaration de l’interface d’une classe, il est possible de définir une visibilité pour les variables d’instances.
Par exemple:
@interface Foo: NSObject
{
@public

id somePublicObject;

@protected

id someProtectedObject;

@private

id somePrivateObject;
}

+ ( void )someStaticMethod;
- ( void )someMemberMethod;

@end
Les variables d’instances peuvent être regroupées en fonction de leur visibilité: publique (accessible depuis l’extérieur), protégée (accessible depuis la classe et les classes filles), et privée (accessible depuis la classe uniquement).
Il n’existe rien de tel pour les méthodes, au niveau de la déclaration de l’interface. Cependant, il est parfaitement possible d’implémenter le concept de méthodes privées, et ce de plusieurs façons.

Solution 1 – Implémentation

La première solution, et sans doute la plus évidente, consiste simplement à déclarer des méthodes dans l’implémentation.
Dans un tel cas, ces méthodes ne seront pas visibles depuis l’extérieur, étant donné qu’elles ne sont pas présentes dans l’interface.
Par exemple:
@interface Foo: NSObject
{}

- ( void )somePublicMethod;

@end

@implementation Foo

- ( void )somePrivateMethod
{}

- ( void )somePublicMethod
{
[ self somePrivateMethod ];
}

@end
Le principal problème avec cette approche est que les méthodes “privées” doivent impérativement être déclarées avant le code y faisant référence, sous peine d’une erreur lors de la compilation.
Dans l’exemple précédent, nous n’aurions pas pu déclarer “somePublicMethod” avant “somePrivateMethod”.
Dans certains cas, lors d’appels cycliques par exemple, il est tout simplement impossible d’utiliser cette solution.

Solution 2 – Fonctions statiques

Une deuxième solution consiste à l’utilisation de fonctions statiques.
En langage C, une fonction statique n’est visible que pour les fonctions se trouvant dans le même fichier.
Il est à noter que, bien que l’on utilise également dans ce cas le mot clé ‘static’, celui-ci à un tout autre sens que si on l’utilise pour une variable, à l’intérieur d’une fonction.
Autrement dit:
@implementation Foo

static void somePrivateFunction( void );
static void somePrivateFunction( void )
{}

- ( void )somePublicMethod
{
somePrivateFunction();
}

@end
Il s’agit d’une fonction, et non d’une méthode. Il est par contre possible, depuis cette fonction, d’accéder aux autres méthodes, et même aux variables d’instances.
Pour réaliser cela, il suffit de passer l’instance en paramètre de la fonction:
@implementation Foo

static void somePrivateFunction( Foo * obj );
static void somePrivateFunction( Foo * obj )
{
/* Appel d'une méthode de la classe */
[ obj someOtherMethod ];

/* Accès à une variable d'instance de la classe */
obj->someInstanceVariable = NULL;
}

- ( void )somePublicMethod
{
somePrivateFunction( self );
}

@end
Une instance d’une classe est tout simplement (ou presque) un pointeur vers une structure C. L’opérateur ‘->’ peut donc parfaitement être utilisé pour accéder à une variable d’instance, qui n’est autre qu’un membre de cette structure.

Solution 3 – Catégories

Une dernière solution, sans doute la plus propre, consiste à utiliser des catégories. Ces dernières sont utilisées pour ajouter des méthodes dans une classe existante.
Par exemple:
@interface Foo: NSObject
{}

- ( void )somePublicMethod;

@end

@interface Foo( private )

- ( void )somePrivateMethod;

@end

@implementation Foo

- ( void ) somePublicMethod
{
[ self somePrivateMethod ];
}

@end

@implementation Foo( private )

- ( void ) somePrivateMethod
{}

@end
Ici, nous définissons l’interface de la classe Foo, ainsi qu’une catégorie nommée “private” (ce nom peut être n’importe lequel).
Nous avons donc ensuite deux implémentations. Une pour les méthodes publiques, et l’autre pour les privées.
Au niveau des headers, seule l’interface de base doit être présente, bien évidemment. La déclaration de la catégorie doit se faire dans le fichier contenant l’implémentation.
Il est à noter qu’il est également possible avec ce système d’utiliser une seule et unique implémentation. Pour ce faire, il suffit de déclarer une catégorie sans nom:
@interface Foo: NSObject
{}

- ( void )somePublicMethod;

@end

@interface Foo()

- ( void )somePrivateMethod;

@end

@implementation Foo

- ( void ) somePublicMethod
{
[ self somePrivateMethod ];
}

- ( void ) somePrivateMethod
{}

@end
Dès lors, puisque les méthodes privées sont définies dans la catégorie, leur ordre de déclaration dans l’implémentation n’a plus aucune importance, contrairement à la première solution présentée ici.

Quelle solution choisir?

Ces trois solutions fonctionnent très bien. Personnellement, j’ai tendance à éviter la première, principalement en raison de l’ordre de déclaration, et aussi parce que les méthodes privées sont difficilement repérables dans le code.
L’utilisation de catégories permet au code de rester clair et lisible, l’implémentation des méthodes privées pouvant se faire dans un autre fichier que l’implémentation des méthodes publiques. Cela permet du coup d’identifier immédiatement la visibilité d’une méthode.
C’est donc cette troisième solution que je préconise, est qui certainement la plus adaptée au langage Objective-C.
Be Sociable, Share!

related articles

comments

  1. Je pensais que tout le monde faisait la troisième méthode. Etant donné que c’est ce que recommande Apple.

    En bref, la version une est pas bonne, parce que le compilateur n’est conscient que la méthode existe qu’au runtime (et je vois pas l’intérêt de faire ça).

    La deux c’est du C, donc c plus vraiment une approche objet. Et donc on zap un peu l’encapsulation. Et en plus on a de l’extra-code :)

    Donc moi j’ai pas mieux que la solution 3; Comme tu dis ;)

add a comment: