self->_maVar =[[unObjet alloc] init];

Bonjour,


 


Aujourd'hui j'étudie l'exemple d'Apple PictureSharing, une application relativement récente : ciblant le 10.8. Dans le fichier ClientAppDelegate.m et dans la méthode - (id)init je tombe sur cette syntaxe 



- (id)init
{
    self = [super init];
    if (self != nil) {
        self->_services = [[NSMutableSet alloc] init];
 
        self->_longStatus = @Click on a service to download its image.;
/// etc
    return self;
}

Quand au fichier d'interface il est réduit au minimum dans ClientAppDelegate.h, tout est dans le fichier d'implémentation avec une catégorie anonyme (aka Class extension). Cette manière de faire est récente et semble être la voix préconisée désormais pour avoir des fichiers d'interface les plus discrets possible. L'encapsulation, l'objet "boà®te noire" ...



@interface ClientAppDelegate () <NSNetServiceBrowserDelegate, NSStreamDelegate>
 
// Outlets
 
@property (nonatomic, strong, readwrite) IBOutlet NSArrayController *   servicesArray;
 
// Actions
 
- (IBAction)tableRowClickedAction:(id)sender;
 
enum {
    kDebugMenuTag = 0x64626720          // == 'dbg ' == 1684170528
};
 
enum {
    kDebugOptionMaskStallReceive       = 0x01,
    kDebugOptionMaskReceiveBadChecksum = 0x02
};
 
- (IBAction)toggleDebugOptionAction:(id)sender;
 
// The user interface uses Cocoa bindings to set itself up based on the following 
// KVC/KVO compatible properties.
 
@property (nonatomic, strong, readonly ) NSMutableSet *         services;
@property (nonatomic, strong, readonly ) NSArray *              sortDescriptors;
@property (nonatomic, strong, readwrite) NSImage *              lastReceivedImage;
@property (nonatomic, copy,   readwrite) NSString *             longStatus;
/// etc
@end

Ce qui me trouble c'est que ça semble marier du préconisé : utiliser les "Class Extension" pour masquer les accès à  une classe, jusqu'à  y mettre les IBOutlets (!) et du "pas conseillé" : accéder directement aux variables d'instance en s'amusant à  dépointer des éléments de structure ce qui supprime justement l'encapsulation, au moins dans la manière de penser !


 


Suis je le seul surpris ou cette nouvelle syntaxe est elle dans la "norme" désormais ?


 


Réponses

  • Personnellement, je mets mes IBOutlets dans la class extension, sauf si j'ai besoin d'exposer un des composants, donc ceci ne me surprend pas. En revanche, je ne déclare jamais de méthodes dans la class extension, je n'en vois pas beaucoup l'utilité (aide à  la conception ?).


    Je n'avais jamais vu la déclaration du support de protocoles dans la class extension, donc c'est bon à  savoir que ça peut se faire.


    Le vrai truc surprenant, c'est la syntaxe avec les -> que je connaissais mais que je n'ai jamais vu utilisée !! L'alliance du moderne et de l'ancien : la mode du vintage ? ;-)
  • tabliertablier Membre
    mars 2014 modifié #3

    C'est comme d'habitude: Il y a les grandes théories, L'encapsulation, L'accès aux variables par des pointeurs, .... etc.  Puis il y a tout ce qu'on peut faire, qui marche, mais qui n'est pas dans le droit fil de la pensée unique, qui est déconseillé voir interdit mais qu'on fait quand même parce que c'est plus simple, plus rapide, ou pour d'autres raisons.


    J'utilise assez souvent la syntaxe avec des -> pour accéder à  des variables.


  • samirsamir Membre
    mars 2014 modifié #4

    Bonjour,


     


    @laudema Y a rien de choquant pour moi dans dans le bout de code Apple que t'as posté.


     




    accéder directement aux variables d'instance en s'amusant à  dépointer des éléments de structure ce qui supprime justement l'encapsulation, au moins dans la manière de penser !

    Ben c'est le pattern conseillé dans les init en tout cas, accéder aux iVars directement durant l'initialisation de l'objet

     



    - (instancetype)initWithName:(NSString *)name {
          self = [super init];
         if (self){
            _name = name;
         }
        return self;
     }


    Pour la syntaxe "self->" :


     


    Y a pas de mal à  l'utiliser ou pas c'est à  peut près la même choses, mais les développeurs Cocoa ont tendance a accéder directement aux ivars sans le "self->" parce que c'est plus facile à  avec "_".


     


    Après techniquement ce n'est pas vraiment la même chose 


     


    Si on écrit pas le "self->myIvars" le compilateur va chercher d'abord si une variable local "myIvars" existe dans la définition de la méthode, sil elle existe pas dans ce cas il va accéder à  la variable d'instance.


     


     


  • samirsamir Membre


     je ne déclare jamais de méthodes dans la class extension, je n'en vois pas beaucoup l'utilité (aide à  la conception ?).




    ça peux être pratique par exemple dans les tests unitaires ( si tu testes les méthodes privées bien sur :))

  • yoannyoann Membre

    Comme dit, ce code est parfaitement juste.


     


    Concernant le init. La documentation indique qu'il est déconseillé d'utiliser les accesseur lorsqu'on est en phase de init. Ces méthode sont câblé pour déclencher automatiquement les système de KVO, chose que l'on ne souhaite pas à  l'init. Il est donc impératif de faire une assignation direct des valeurs des variables d'instance.


     


    Normalement on fait _foo = bar. Pour autant la syntaxe self->_foo = bar est également juste. Self est une structure dont les ivar sont les membres. ça fonctionne.


     


    Pour le fichier d'interface public, il est destiné à  contenir ce qui est utilisable par un code client. Selon l'utilité de la classe il peut ne rien avoir dedans. Par exemple avec les contrôleurs.


     


    On retrouve dans la catégorie anonyme la définition des propriétés et méthodes à  usage interne et pour InterfaceBuilder via les IBOutlet / IBAction.


     


    Bref, c'est un code tout à  fait correct et moderne.


  • AliGatorAliGator Membre, Modérateur
    Il n'y a rien de choquant, tout est du préconisé :

    1. Mettre le maximum des @property dans la Class Extension dans le .m, pour ne pas exposer dans l'API publique ce qui n'a pas de raison d'être exposé à  l'extérieur
    2. Les IBOutlets ne font pas exception à  la règle (perso je met toujours tous mes IBOutlets dans l'extension de classe et pas dans le .h)
    3. Dans les méthodes d'init, Apple conseille d'utiliser les ivar plutôt que les propriétés (genre _ivar ou self->_ivar et pas self.prop), car utiliser les setters des propriétés peut avoir des effets de bord (en particulier déclencher le KVO ou des choses comme ça) alors qu'on est dans l'init.

    L'un des effets de bord présenté par Apple pour expliquer pourquoi les méthodes d'init sont les rares cas où il faut potentiellement plutôt utiliser les ivars que les propriétés, c'est parce que dans l'init, par définition, toutes les données ne sont pas encore initialisées à  une valeur de départ (puisque c'est justement ce que tu es pile en train de faire dans ton "init"). Du coup si une variable A dépend d'une variable B (et potentiellement vice-versa), si tu utilises le setter de A alors que tu n'as même pas encore donné de valeur initiale à  B alors que A va en dépendre, ça peut ne pas faire ce que tu avais prévu.

    Utiliser les ivars plutôt que les propriétés et donc leurs setters va permettre de shinter/court-circuiter cela dans la méthode d'init, ce qui peut se justifier parce que tout n'est pas encore totalement initialisé tant que tu n'as pas exécuté toute la méthode init.

    Après, ce point spécifique dépend un peu de chacun. Si tu connais ces effets de bord et que tu maà®trises totalement ta classe et fais les choses en connaissance de cause, et si tu sais qu'il n'y a pas de dépendance et de risques que tes propriétés dépendent les unes des autres, alors ce n'est pas si problématique que ça et tu peux utiliser les propriétés donc les setters. Mais sinon, juste pour la méthode d'init, ça peut quand même être plus sécurisant d'utiliser des ivars (mais d'utiliser les propriétés dans l'implémentation des autres méthodes que init où par contre là  ton objet a un état stable et initialisé).
  • Merci à  tous pour vos réponses, j'en tiendrais compte à  l'avenir.


Connectez-vous ou Inscrivez-vous pour répondre.