Objet immutable en public, mutable en privé

2»

Réponses

  • tabliertablier Membre
    février 2013 modifié #32
    Je suis contraint de programmer pour 10.4, 10.5, 10.6 et 10.7. et pas mal de mes sources ont été écrits sous 10.3, 10.4 ou 10.5. Je n'ai pas trop le choix! Pour les risques, j'en suis conscient, et pour la gestion de la mémoire, je continue de la faire "à  la main". Donc, de temps en temps je passe 1/2 heure à  1 heure à  chercher le pourquoi de l'accident. C'est formateur sur ce sujet!

    Pour IOS: je ne pratique pas, mais je n'aurais pas du le barrer! sorry!

    Quand aux nouvelles manières de programmer, je finirai par m'y mettre, même si je trouve qu'elles sont parfois un peu abstruses et n'apportent pas forcément grand chose par rapport aux anciennes (à  part une économie du clavier).
  • Pourquoi ne pas déclarer un tableau en static (entre les #import et @implementation), une variable globale quoi.

    Longtemps j'ai voulu éviter au titre que "c'est sale" mais bon, à  l'usage c'est bien pratique, pour la mémoire un NSMutableArray qui disparait avec l'application n'est pas si lourd. Le tout est de ne pas avoir besoin d'une version différente entre chaque instance.

    Et du coup il devient très simple de retourner une copie non mutable de cet objet ..








    <br />
    [color=#703DAA][font=Menlo][size=2]<br />
    [color=#bb2ca2]#import MaClasse.h[/color][/size][/font][/color]<br />
    [color=#703DAA][font=Menlo][size=2]<br />
    [color=#bb2ca2]static[/color][color=#000000] [/color]NSMutableArray[color=#000000] *myMutableAr;[/color][/size][/font][/color]<br />
    <br />
    [color=#703DAA][font=Menlo][size=2]<br />
    [color=#000000]@implementation {[/color][/size][/font][/color]<br />
    [color=#703DAA][font=Menlo][size=2]<br />
    @dynamic nonMutable;[/size][/font][/color]<br />
    [color=#703DAA][font=Menlo][size=2]<br />
    [color=#000000]+ (void)initialize{[/color][/size][/font][/color][font=Menlo][size=2]<br />
    [color=#000000]    [/color][color="#703daa"]myMutableAr = [[NSMutableArray alloc]init];[/color][/size][/font][font=Menlo][size=2]<br />
    [color=#703DAA]}[/color][/size][/font][font=Menlo][size=2]<br />
    - (NSArray*)nonMutable{[/size][/font][font=Menlo][size=2]<br />
        return [[myMutableAr copy]autorelease];[/size][/font][font=Menlo][size=2]<br />
    }[/size][/font]<br />
    [font=Menlo][size=2]<br />
    @end[/size][/font]<br />
    <br />
    




    Par contre initialize n'a pas de pendant quand on a fini d'utiliser une classe
  • AliGatorAliGator Membre, Modérateur
    Mais pourquoi, pourquoi, pourquoi ?



    (part vomir à  côté)
  • laudemalaudema Membre
    février 2013 modifié #35
    'AliGator' a écrit:


    (part vomir à  côté)


    Petite nature va image/rolleyes.gif' class='bbc_emoticon' alt='::)' />

    Mais pendant que tu te presses le citron(*) pour les éviter moi j'ai trois classes qui ont un tableau en variable globale et ça ne m'empêche pas plus de vivre que ça n'empêche mon appli de fonctionner correctement.

    Sinon, justement, ce matin, je visionnais une des videos d'Apple sur iTunes U (Session 232 - Objective-C Advancements in Depth" de l'année 2011) et il sera désormais possible d'inscrire des variables dans les catégories anonymes. Ce qui résoudra le problème à  la satisfaction générale, loué soit Apple image/implore.gif' class='bbc_emoticon' alt=' o:) ' />



    (*)dans les tourne-autour qui vont bien :




    <br />
    [myMutable writeToURL:myAppSupportDir/myMutable.plist atomicaly:NO];<br />
    myMutable = [[NSMutableCollection objectWithContentOfURL:myAppSupportDir/myMutable.plist]mutableCopy];<br />
    


    et
    <br />
    [[NSUserDefaults standardUserDefaults] setObject:myMutable forKey:@&quot;myMutable&quot;];<br />
    myMutable =[[[NSMutableCollection alloc] initWithObject:[[NSUserDefaults standardUserDefaults] objectForKey:@&quot;myMutable&quot;]]mutableCopy]<br />
    


    Tout dépend de la fréquence d'appel de myMutable dans le code ..
  • AliGatorAliGator Membre, Modérateur
    février 2013 modifié #36
    'laudema' a écrit:


    Mais pendant que tu te presses le citron(*) pour les éviter moi j'ai trois classes qui ont un tableau en variable globale et ça ne m'empêche pas plus de vivre que ça n'empêche mon appli de fonctionner correctement.
    Parce que tu n'es pas encore tombé dans des cas de racing conditions (qui par définition sont pas reproductibles aisément, ça dépend de tellement de choses, de la charge processeur, de la rapidité de l'appareil, etc... et d'avoir pas de bol de tomber sur 2 accès concurrents à  la même ressource quasi en même temps) mais c'est pas parce que c'est rare que ça n'arrive pas


    'laudema' a écrit:
    Tout dépend de la fréquence d'appel de myMutable dans le code ..
    Bah oui justement, c'est tout le problème. Dans 95% des cas, tu auras la chance que ça passe. Mais si tu prends l'habitude de coder comme ça et surtout le jour où tu vas tomber dans les 5% et passer 2j à  déboguer (car les racing conditions et les bugs dus au multithreading, en plus d'être difficilement reproductible, sont parmi les plus difficiles à  trouver et corriger), tu retiendras la leçon !


    'laudema' a écrit:
    Sinon, justement, ce matin, je visionnais une des videos d'Apple sur iTunes U (Session 232 - Objective-C Advancements in Depth" de l'année 2011) et il sera désormais possible d'inscrire des variables dans les catégories anonymes. Ce qui résoudra le problème à  la satisfaction générale, loué soit Apple image/implore.gif' class='bbc_emoticon' alt=' o:) ' />
    Oui c'est une nouveauté d'Objective-C relativement récent, mais qui en pratique ne m'a pas changé grand chose puisque je ne déclare plus de variables d'instance depuis des lustres (4, 5 ans ?). Les seules variables d'instance que je déclare c'est soit des bit-fields pour optimiser mes respondsToSelector quand j'ai un delegate avec des méthodes @optional que j'appelle souvent (voir la discussion qu'on avait eu avec ldesroziers à  l'époque sur un autre sujet), et quand j'ai besoin de variables de l'environnement CoreFoundation où il est nécessaire de gérer la mémoire soi-même à  coup de CFRetain/CFRelease et de faire de la gestion mémoire à  la main aux petits oignons quand on descend bas niveau... et encore ça reste très rare.



    Dans 99% des cas, je n'ai jamais besoin de déclarer de variable d'instance, ni dans l'API publique dans le .h ni dans l'API privée dans le .m dans une extension de classe (ce que je suppose que tu appelles "catégorie anonyme" bien que ce terme n'existe pas officiellement, d'autant que malgré la syntaxe proche pour les déclarer avec les parenthèses, il y a une différence majeure entre les catégories et les extensions de classe qui est la vérification d'implémentation par le compilateur, ce qui n'est pas superflu parfois). Puisque dans 99% des cas, une @property suffit, qu'elle soit dans l'@interface publique de la classe (.h) ou dans l'extension (@interface MyClass() privée), est plus sécurisée car thread-safe, génère le KVO, gère la politique mémoire, et répond aux exigences du KVC (alors que l'accès aux ivars internes peut être fermé au KVC via "+accessInstanceVariablesDirectly" par exemple), et plein d'autres subtilités.
  • 'AliGator' a écrit:


    Parce que tu n'es pas encore tombé dans des cas de racing conditions ...

    ..mais c'est pas parce que c'est rare que ça n'arrive pas


    C'est vrai, mais pour l'instant je n'ai pas besoin de multi-threading ni d'accès concurrentiels. Et je crois que je les éviterais aussi longtemps que je pourrais parce que j'ai essayé, par curiosité, mais vu la "prise de tête" que ça représente j'ai trouvé plus sage et heureux de m'en passer. Par contre j'ai besoin de rester en 32 bits donc j'ai des variables d'instance que je retain | release, et quand c'est vraiment nécessaire j'ajoute une globale. C'est pas une habitude non plus hein, juste que c'est plus confortable.

    Et dans le cas du premier post de ce fil, une globale static me parait la solution la plus simple, certainement pas la plus élégante.

    A lire l'article cité ailleurs http://collection.sasfepu.free.fr/atari/dave.small/mbti.html il est probable que je suis plus un NF et toi un NT, ceci expliquant probablement pourquoi nous avons du mal à  nous entendre image/wink.png' class='bbc_emoticon' alt=';)' />
  • CéroceCéroce Membre, Modérateur
    'laudema' a écrit:


    Et dans le cas du premier post de ce fil, une globale static me parait la solution la plus simple, certainement pas la plus élégante.


    Il ne pourrait alors plus y avoir qu'une seule instance de la classe. De plus, je ne vois pas l'avantage comparé à  une variable d'instance.
  • AliGatorAliGator Membre, Modérateur
    février 2013 modifié #39
    'laudema' a écrit:
    C'est vrai, mais pour l'instant je n'ai pas besoin de multi-threading ni d'accès concurrentiels.
    Mouais, j'en doute, à  mon avis tu en as quand même même sans le savoir explicitement. Car sinon ça voudrait dire que ton application serait en permanence en train de bloquer l'interface utilisateur... A part si tu fais genre juste une calculatrice ou un outil du genre, dès que tu as des accès réseau ou même une écriture disque ou des choses comme ça tu dois faire attention à  ne pas bloquer l'UI (perso sinon si je vois une appli qui a l'air de geler sans arrêt et me donne l'impression d'être plantée parce qu'elle bloque l'UI et que j'ai la roue multicolore, elle va rapidement finir à  la poubelle...)



    En plus, surtout, le multithreading peut faire peur et si tu rentres dans les détails c'est pas si compliqué si tu respectes des principes de base (faire des accesseurs atomiques, connaà®tre les risques classiques, utiliser les technos faites pour te faciliter la vie sur ces sujets comme NSOperationQueue ou GCD...)
    'laudema' a écrit:
    et quand c'est vraiment nécessaire j'ajoute une globale. C'est pas une habitude non plus hein, juste que c'est plus confortable.
    J'ai du mal à  voir en quoi c'est plus confortable d'avoir une globale que d'avoir une variable d'instance ou une propriété...
    'laudema' a écrit:
    Et dans le cas du premier post de ce fil, une globale static me parait la solution la plus simple, certainement pas la plus élégante.
    Ce n'est pas plus long de déclarer une ivar qu'une globale, sauf qu'une variable globale pose bien plus de problèmes, tant sur la protection d'accès pas les autres objets (même en monothread) que sur du MT, alors qu'une ivar a moins ce pb de protection d'accès et une @property a en plus cette protection MT (entre autres, en plus de tous les autres avantages déjà  exposés maintes fois ici et dans d'autres posts). Et une globale te bloque à  une seule instance comme le souligne Céroce, en plus.



    Donc vraiment, moi non plus je ne vois pas l'avantage comparé au moins à  une variable d'instance, même sans parler de multithreading.
  • laudemalaudema Membre
    février 2013 modifié #40
    Pour l'instant mon interface utilisateur reste fluide assez pour moi, et comme je suis le seul user je n'ai pas de réclamation.

    Mais crois moi que si je me suis fatigué à  apprendre à  programmer c'est pas pour rester les bras croisés pendant que ma machine mouline pour ses accès disque !

    Mes accès réseaux sont synchrones parce que je l'ai voulu (en Cocoa c'est pas documenté hors usage asynchrone de toutes façons, faut le vouloir vraiment image/wink.png' class='bbc_emoticon' alt=';)' />/> mais c'est pour un accès à  une autre application sur le même poste et quand je la sollicite la seule chose qui m'intéresse c'est sa réponse, donc ça ne me dérange pas d'attendre, s'il le faut. Mettre un timer qui tourne et attendre la notification en asynchrone ne m'apporterait pas le même sentiment du temps suspendu en attente de l'heureux événement (à  la fin j'ai une facture estampillée CNDA qui sera payée dans les 8 jours image/smile.png' class='bbc_emoticon' alt=':)' />/>.

    Comme quoi les besoins ne sont pas uniformes ..

    Quand à  une globable statique sa portée c'est le fichier : elle est initialisée dans la méthode de classe + initialize et partagée par toutes les instances. Pourquoi devrais ne pourrais je avoir qu'un singleton dans ma classe ?

    Imaginons une application avec une classe pour laquelle je ne veux pas avoir deux objets avec le même nom

    Interface :
    <br />
    #import &lt;foundation foundation.h=&quot;&quot;&gt;<br />
    @interface SingleNamedValue : NSObject{<br />
        NSValue *myValue;<br />
        NSString *myName;<br />
    }<br />
    - (id)initSingleValue:(NSValue*)value withName:(NSString*)name;<br />
    @end<br />
    


    Implementation :
    <br />
    #import &quot;SingleNamedValues.h&quot;<br />
    static NSMutableArray *namedValues;<br />
    @implementation SingleNamedValue<br />
    + (void)initialize{<br />
        namedValues = [[NSMutableArray alloc] init];<br />
    }<br />
    - (id)init{<br />
        NSLog(@&quot;utilisez -(id) newUniqueValue:(NSValue *)value withName :(NSString *)name&quot;);<br />
        NSBeep();<br />
        return nil;<br />
    }<br />
    - (id)initSingleValue:(NSValue *)value withName :(NSString *)name{<br />
        self = [super init];<br />
        if (nil &#33;= self) {<br />
    	    myValue = value;<br />
    	    myName = name;<br />
    	    NSUInteger i = [namedValues indexOfObject:self];<br />
    	    if (NSNotFound == i) {<br />
    		    [namedValues addObject:self];<br />
    		    return self;<br />
    	    }<br />
    	    [self release];<br />
    	    return [[namedValues objectAtIndex:i]retain];<br />
        }<br />
        return self;<br />
    }<br />
    @end<br />
    


    après je suppose qu'avec des @synchronize dans le init on peut même arriver à  faire du multi-threading mais je ne m'y suis jamais risqué ..

    Sinon je ne connais pas plus simple comme manière de faire et je ne vois pas pourquoi déclarer et garder ailleurs que dans une globale static de ce fichier le tableau d'objets déjà  créés.</foundation>
  • laudemalaudema Membre
    février 2013 modifié #41
    Oups, j'ai dit une bêtise: mon propos se rapporte à  une variable de classe, pas une variable d'instance. image/whip.gif' class='bbc_emoticon' alt=' >:) ' />



    MAIS il est désormais en effet possible de déclarer des variables d'instance cachée grâce aux catégories anonymes / extension








    It's also possible to use a class extension to add custom instance variables. These are declared inside braces in the class extension interface:



    @interface XYZPerson () {

    id _someCustomInstanceVariable;

    }

    ...

    @end




    Use Class Extensions to Hide Private Information

    Et puisqu'on utilise la dernière version du compilateur on pouvait déjà  lui demander d'en synthétiser une tout seul comme un grand










    @interface XYZPerson ()

    @property NSObject *extraProperty;

    @end


    the compiler will automatically synthesize the relevant accessor methods, as well as an instance variable, inside the primary class implementation.




    PS : l'exemple d' Apple est tout aussi fautif que celui de Tablier : faute de déclaration la NSString est certes requalifiée en readwrite mais reste assign












    In order for the XYZPerson class to be able to change the property internally, it makes sense to redeclare the property in a class extension that's defined at the top of the implementation file for the class:



    @interface XYZPerson ()

    @property (readwrite) NSString *uniqueIdentifier;

    @end



    @implementation XYZPerson

    ...

    @end





  • AliGatorAliGator Membre, Modérateur
    février 2013 modifié #42
    'laudema' a écrit:


    MAIS il est désormais en effet possible de déclarer des variables d'instance cachée grâce aux catégories anonymes / extension



    Use Class Extensions to Hide Private Information
    Bah oui bien sûr ça c'est ce que j'arrête pas de dire et que je fais tous les jours, mon .h ne contient que l'API publique, et mon .m contient une extension de classe pour déclarer mon API privée, c'est ce que je préconise !! (Et c'est ce qu'Apple préconise aussi depuis un bail)



    Au final par exemple même mes @property IBOutlets et IBAction sont déclarés dans mon .m dans l'extension de classe, car l'extérieur n'a pas à  connaà®tre les IBActions qui sont déclenchés par clic/tap de bouton ni les IBOutlets dont j'ai besoin pour manipuler mon XIB

    (de l'extérieur je vais jamais écrire monVC.productNameLabel.text = xx mais plutôt monVC.productName = xx et c'est le setter de productName qui affectera le texte du label, comme ça si je change ma vue (V) et affiche ça dans un UITextField ou un autre truc un jour, j'aurais juste à  adapter le code dans le .m de mon ViewController, sans toucher au reste de l'appli)
  • Je fais pareil que toi Ali et j'avoue que mon .h est quasiment vide maintenant. On voit tout de suite ce que l'on désire montrer aux autres c'est carrément plus clair.
  • AliGatorAliGator Membre, Modérateur
    Bah en même temps c'est finalement le but premier d'un ".h" : déclarer l'API publique, déclarer uniquement ce que l'extérieur a besoin de savoir. Genre quand on fait une librairie ou un framework, que celui qui va utiliser la lib pour le fmk sache la liste des méthodes qu'il peut appeler. Le reste (les variables ou propriétés internes, les méthodes privées, ...) l'utilisateur de la lib (ou de ta classe en général) n'a pas à  le savoir.
  • Oui oui je suis bien d'accord. Mais quand je lis le code de plusieurs personnes bah c'est pas vraiment appliqué ainsi ...
  • AliGatorAliGator Membre, Modérateur
    Bah non parce que y'a plein de personnes qui code comme les pieds...
  • et en général ça pue image/biggrin.png' class='bbc_emoticon' alt=':D' />
  • 'xyloweb' a écrit:


    et en général ça pue image/biggrin.png' class='bbc_emoticon' alt=':D' />


    C'est une autre interprétation du "code smell" de Martin Fowler.
  • Moi c'est le code spaghetti que je trouve indigeste, surtout celui codé avec les pieds !
  • AliGatorAliGator Membre, Modérateur
    Oui mais ne pas séparer l'API publique de l'API privée proprement, et utiliser des ivar au lieu de @property, ou ce genre de choses, si ça peut être anodin pour un petit projet, dès qu'il commence à  grossir, ça fait justement vite du code spaghetti, parce qu'à  ne pas avoir une architecture et une organisation de code propret et à  force de modifier des choses dans tous les sens, si la base n'est pas solide, tout s'écroule. Un vrai Jenga !



    Au début du projet vous vous dites "bah c'est pas grave, je respecte pas pile poil les trucs dans le détail, mes accès à  mes variables ne déclenchent pas de KVO, mes classes ne sont pas thread-safe, mais bon je m'en fiche c'est pour mon boui-boui à  moi vite fait", mais quand votre projet s'étoffe, vous n'y pensez plus et c'est là  qu'à  force d'ajouter des choses dans tous les coins y'a plus rien qui marche ensemble image/wink.png' class='bbc_emoticon' alt=';)' />
Connectez-vous ou Inscrivez-vous pour répondre.