Variables globales et debugger
tablier
Membre
[size=9pt] Les questions sont à la fin.
J'ai plusieurs objets (pour l'exemple disons 2) qui utilisent une vingtaine de variables communes (pour l'exemple une seule), sans faire appel réciproquement à des méthodes des autres objets. L'un des objets initialise toutes les variables et les autres les utilisent. Chaque objet est déclaré sous IB et donc instancié au lancement. La première idée qui vient à l'esprit pour accéder aux variables est d'ajouter des IBOutlets entre objets et d'écrire des accesseurs. Principe:[/size]
[size=9pt]Cela marche bien, à ceci près que cela finit par être pénible avec un grand nombre de variables. Une solution proche est d'écrire un seul accesseur qui renvoie l'ensemble des variables dans un NSArray ou un NSDictionary. Ce n'est guère plus simple et tout aussi ch...t à utiliser.
Une deuxième solution est de déclarer public les variables et d'utiliser un pointeur sur l'objet qui les contient, comme ceci:[/size]
[size=9pt]Cela marche bien, mais l'écriture répétée du pointeur est tout aussi pénible que précédemment!
Dans le cas qui m'occupe, le "K & R" conseille de déclarer de manière "globale" les variables dans un seul fichier et d'utiliser "extern" dans les autres fichiers. Comme j'ai un grand nombre de variable, j'ai ajouté un .h (qui contient les déclarations "extern") et je l'importe dans les fichiers ou j'utilise les variables. comme ceci:[/size]
[size=9pt]Cette méthode marche bien et est très économique en écriture! OUI, MAIS: je n'arrive pas à voir la variable globale "varCommune" dans le debugger! même en mettant un point d'arrêt sur "varCommune = 12". Je ne peux donc pas examiner le contenu de mes variables globales en cours de debugging!!
- Que dois-je faire pour voir ces variables dans le debugger?
- Existe-t-il une autre organisation que les trois ci-dessus, qui permettent à +sieurs objets d'accéder à des variables communes, avec une syntaxe très, très simple? [/size]
J'ai plusieurs objets (pour l'exemple disons 2) qui utilisent une vingtaine de variables communes (pour l'exemple une seule), sans faire appel réciproquement à des méthodes des autres objets. L'un des objets initialise toutes les variables et les autres les utilisent. Chaque objet est déclaré sous IB et donc instancié au lancement. La première idée qui vient à l'esprit pour accéder aux variables est d'ajouter des IBOutlets entre objets et d'écrire des accesseurs. Principe:[/size]
[size=9pt]// A.h : __________________________
#import <Cocoa/Cocoa.h>
@interface ObjetA : NSObject {
int varCommune ;
}
- (void)metValeur ;
- (int)varCommune ;
@end
// A.m : __________________________
#import "A.h"
@implementation ObjetA
- (void)metValeur { varCommune = 12 ; }
- (int)varCommune { return = varCommune ; } // l'accesseur
@end
// B.h : __________________________
#import <Cocoa/Cocoa.h>
@class objetA
@interface ObjetB : NSObject {
IBOutlet objetA *leA ;
int locale ;
}
- (void)prendValeur ;
@end
// B.m : __________________________
#import "B.h"
@implementation ObjetB
- (void)prendValeur
{ locale = [leA varCommune] ; }
@end [/size]
[size=9pt]Cela marche bien, à ceci près que cela finit par être pénible avec un grand nombre de variables. Une solution proche est d'écrire un seul accesseur qui renvoie l'ensemble des variables dans un NSArray ou un NSDictionary. Ce n'est guère plus simple et tout aussi ch...t à utiliser.
Une deuxième solution est de déclarer public les variables et d'utiliser un pointeur sur l'objet qui les contient, comme ceci:[/size]
[size=9pt]// A.h : __________________________
#import <Cocoa/Cocoa.h>
@interface ObjetA : NSObject {
@public
int varCommune ;
}
- (void)metValeur ;
@end
// A.m : __________________________
#import "A.h"
@implementation ObjetA
- (void)metValeur
{ varCommune = 12 ; }
@end
// B.h : __________________________
#import <Cocoa/Cocoa.h>
@class objetA
@interface ObjetB : NSObject {
IBOutlet objetA *leA ;
int locale ;
}
- (void)prendValeur ;
@end
// B.m : __________________________
#import "B.h"
@implementation ObjetB
- (void)prendValeur
{ locale = leA->varCommune ; }
@end [/size]
[size=9pt]Cela marche bien, mais l'écriture répétée du pointeur est tout aussi pénible que précédemment!
Dans le cas qui m'occupe, le "K & R" conseille de déclarer de manière "globale" les variables dans un seul fichier et d'utiliser "extern" dans les autres fichiers. Comme j'ai un grand nombre de variable, j'ai ajouté un .h (qui contient les déclarations "extern") et je l'importe dans les fichiers ou j'utilise les variables. comme ceci:[/size]
[size=9pt]// A.h : __________________________
#import <Cocoa/Cocoa.h>
@interface ObjetA : NSObject {
}
- (void)metValeur ;
@end
// A.m : __________________________
#import "A.h"
int varCommune ;
@implementation ObjetA
- (void)metValeur
{ varCommune = 12 ; }
@end
// Commun.h : ____________________ fichier ajouté
extern int varCommune ;
// B.h : __________________________
#import <Cocoa/Cocoa.h>
@interface ObjetB : NSObject {
int locale ;
}
- (void)prendValeur ;
@end
// B.m : __________________________
#import "B.h"
#import "Commun.h"
@implementation ObjetB
- (void)prendValeur
{ locale = varCommune ; }
@end [/size]
[size=9pt]Cette méthode marche bien et est très économique en écriture! OUI, MAIS: je n'arrive pas à voir la variable globale "varCommune" dans le debugger! même en mettant un point d'arrêt sur "varCommune = 12". Je ne peux donc pas examiner le contenu de mes variables globales en cours de debugging!!
- Que dois-je faire pour voir ces variables dans le debugger?
- Existe-t-il une autre organisation que les trois ci-dessus, qui permettent à +sieurs objets d'accéder à des variables communes, avec une syntaxe très, très simple? [/size]
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
La définir dans le fichier _Prefix.pch
Que dois-je mettre dans le _Prefix.pch? les déclarations des variables ou bien les "extern ....." ? Si c'est les "extern .....", ou devrais-je mettre les déclarations?
Faut-il importer le _Prefix.pch dans tout les sources, ou cela est-il fait par défaut?
Merci d'avance.
NSString * commonVar;
et rien d'autre : aucune variable extern, aucun #import
Ce fichier est en quelque sorte automatiquement inclus dans chaque fichier d'en-têtes
Mais on peut aussi regrouper ces variables globales dans un fichier MesVariablesGlobales.h
et faire #import "MesVariablesGlobales.h" dans le fichier _Prefix.pch
Pour partager "proprement" des données communes en langage objet on utilise un singleton.
L'idée est simple. Dans le fichier .m de la classe du singleton on a une instance statique. Lorsqu'on appel un constructeur de commodité du genre [MAClasse defaultManager] cela créé l'instance statique de la classe si la méthode est lancée pour la première fois et sinon cela retourne directement l'instance. Donc quelque soit l'instance qui appelle "defaultManager", on accède toujours au même objet.
On retrouve ce principe dans de nombreuses classes de cocoa:
- [NSFileManager defaultManager]
- [NSNotificationCenter defaultCenter]
- etc
De là à implémenter des accesseurs pour manipuler tes variables communes, il n'y a qu'un pas. Pour faire encore plus générique, on peut implémenter un simple "setValue:forKey:" et "valueForKey:". On passe alors des objets qu'on stocke dans un NSMutableDictionary.
Propre, simple, efficace, évolutif et ré-utilisable. Que demander de plus?
Plus de lecture sur le site d'Apple...
http://developer.apple.com/documentation/Cocoa/Conceptual/CocoaFundamentals/CocoaObjects/chapter_3_section_10.html
Oui je connais la notion de singleton.
Ok, mais quelle est ton argumentation sur ton bannissement ? Parce que le mot "propre" je l'entends depuis 30 ans à l'endroit, à l'envers. Alors je veux bien te croire, je veux bien apprendre, mais énonce clairement les avantages et les inconvénients de telle ou telle méthode.
Voilà une bonne question.
Aller, je mouille ma plume et je m'explique.
Propre, pourquoi?
Parce que un seul objet (le singleton) manipule l'ensemble des données. L'un des principaux reproche que l'on peut faire à un langage non objet c'est justement qu'on ne sait pas qui fait quoi sur des variables globales. C'est donc potentiellement dangereux et délicat à maintenir dans le temps. L'utilisation d'un singleton permet de centraliser l'accès aux variables. Sans même parler de language objet c'est déjà quelque chose qu'on reproduit fréquemment en C (une variable static dans un fichier et une fonction pour y accéder en lecture et une autre en écriture).
Simple et efficace, pourquoi?
Tu l'as toi même montré dans ton exemple. Des variables globales dans tous les sens, cela se complique très très vite. Avec un Singleton, la complexité n'augmente pas. Si tu sais gérer l'accès à une variable, tu sais le faire pour N.
Evolutif, pourquoi?
Allons plus loin et disons maintenant que je veux rendre mon projet "thread safe" car j'effectue des traitements multiple en tâche de fond. Pas de problème, j'ajoute un simple NSLock pour sécuriser l'accès à "setValue:forKey:" et "valueForKey:" et le tour est joué. C'est un exemple parmi d'autres mais l'idée est là .
Ré-utilisable, pourquoi?
Et bien si mon singleton est générique comme je l'ai suggéré en utilisant des méthodes telles que "setValue:forKey:" et "valueForKey:" je peux le rajouter à ma besace de code et le ressortir dans mon projet suivant. Dans le sens inverse, si le problème se repose avec un autre projet, dans le cas des variables globales je vais faire quoi? Tout recoder et perdre du temps...
Voilà , c'est une manière de faire. On peut bien sûr faire comme on veut mais sur de nombreux projets cette méthode a fait ses preuves.
NB: je ne bannis pas les variables globales mais l'accès dangereux qui en résulte.
Sur la centralisation, le setValue: forKey:, le côté générique et facile à transporter, on pourrait penser à une variable globale de type NSMutableDictionary , mais se pose effectivement le problème de l'initialisation, du thread safe, qui est bien gérée par le singleton.
En plus, cela devrait répondre au problème du debugger ...
Thank you, Mister Mala
Si l'on veut aller au bout de la critique, il y a bien sûr un reproche que l'on peut faire au principe de singletons: les performances sont moindres qu'un accès direct à une variable globale puisque l'on s'impose une couche supplémentaire. Mais dans la pratique, les avantages l'emporte la plupart du temps sur cet inconvénient.
Il suffit de mettre ces valeurs lorsqu'on construit le singleton, dans le code de l'accesseur à l'instance unique (defaultManager/defaultCenter/defaultInstance/.... ou quel que soit son nom).
Alors qu'avec des variables globales, on ne sait jamais trop où mettre les valeurs initiales selon l'endroit où l'on utilise ces variables la première fois, etc...
Je ne connaissais pas le singleton!
Et c'est carrément plus simple qu'en C++
Par contre, je trouve leur code de "sharedManager" très mal optimisé...
Il pose un mutex à chaque appel !
==>
Je reviens sur ce poste, car ébloui 8--) à l'époque par l'idée du singleton pour stocker des variables/constantes globales, je gardais la question suivante en réserve : Pourquoi le Prefix_pch serait une mauvaise idée pour glisser des macros, et des constantes ?
D'ailleurs pourquoi ce fichier est-il proposé si on ne l'utilise jamais?
Juste un mot pour vous signaler un article récent sur ce thème des variables globales et des singletons sur le blog Cocoa with Love. L'article s'appelle Singletons, AppDelegates and top-level data.