Variables globales et debugger

tabliertablier Membre
11:13 modifié dans API AppKit #1
[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]//  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]

Réponses

  • Philippe49Philippe49 Membre
    11:13 modifié #2
    dans 1216139219:

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


    La définir dans le fichier _Prefix.pch
  • tabliertablier Membre
    11:13 modifié #3
    j'ai beau essayer, je plante magnifiquement!!  >:(
    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.
  • Philippe49Philippe49 Membre
    11:13 modifié #4
    Tu ne mets dans le fichier _Prefix.pch que
    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
  • MalaMala Membre, Modérateur
    11:13 modifié #5
    Mon dieu quel massacre!!! :brule: Arrêtes tout! Pitié pas le pch en plus!!!  :)

    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? :p

    Plus de lecture sur le site d'Apple...
    http://developer.apple.com/documentation/Cocoa/Conceptual/CocoaFundamentals/CocoaObjects/chapter_3_section_10.html
  • Philippe49Philippe49 Membre
    11:13 modifié #6
    dans 1216154977:

    Mon dieu quel massacre!!! :brule: Arrêtes tout! Pitié pas le pch en plus!!!  :)

    Pour partager "proprement" des données communes en langage objet on utilise un singleton.

    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.

  • Philippe49Philippe49 Membre
    11:13 modifié #7
    Quelle est la justification, la fonctionnalité d'une "variable globale" ou de quelque chose du genre dans un programme ?
    • Il y en a : par exemple, NSFontAttributeName, NSApp, ..
    • Faut-il en rajouter ? personnellement, j'ai toujours pu faire autrement 

    Voilà  une bonne question.
  • MalaMala Membre, Modérateur
    juillet 2008 modifié #8
    dans 1216154977:

    Propre, simple, efficace, évolutif et ré-utilisable. Que demander de plus? :p


    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.
  • Philippe49Philippe49 Membre
    11:13 modifié #9
    Convaincant, et la partie sur le thread safe est assez décisive.
    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    :p
     


  • MalaMala Membre, Modérateur
    11:13 modifié #10
    dans 1216155691:

    ...et les inconvénients de telle ou telle méthode.

    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.
  • AliGatorAliGator Membre, Modérateur
    11:13 modifié #11
    Sans parler du fait que la solution du singleton permet d'initialiser la valeur des variables facilement.
    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...
  • tabliertablier Membre
    11:13 modifié #12
    Merci à  tous pour ces informations et à  Mala particulièrement.  :P
    Je ne connaissais pas le singleton!
  • schlumschlum Membre
    juillet 2008 modifié #13
    Il et bien l'exemple de singleton d'Apple, thread safe et tout 

    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 !

    ==>
    + (MyGizmoClass*)sharedManager<br />{<br />	if(sharedGizmoManager==nil) {<br />		@synchronized(self) {<br />			if(sharedGizmoManager==nil)<br />				[[self alloc] init]; // assignment not done here<br />		}<br />	}<br />	return sharedGizmoManager;<br />}
    
  • Philippe49Philippe49 Membre
    novembre 2008 modifié #14
    dans 1216154977:

    Mon dieu quel massacre!!! :brule: Arrêtes tout! Pitié pas le pch en plus!!!  :)


    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?
  • mouvicielmouviciel Membre
    11:13 modifié #15
    Bonsoir,

    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.
Connectez-vous ou Inscrivez-vous pour répondre.