Déclaration anticipée ( @class, @protocol) et importation des fichier.h

Hello,



J'ai lu quelque part ( désolé je ne me souviens pas de la référence image/rolleyes.gif' class='bbc_emoticon' alt='::)' /> ) : "Pour éviter des dépendances cycliques des fichiers d'entête il est necessaire de recourir à  la déclaration forward des classes ( @class) lorsqu'on a juste besoin de spécifier leur existence et non leur définition".



Mon problème :

"Les dépendances cycliques " ?.



Quand je code, dés fois j'utilise par exemple : @class MyClass; pour dire au compilateur ( et linker) comme quoi MyClass est un nom de class, dans ce cas il va se calmer image/clap.gif' class='bbc_emoticon' alt=' :D ' /> . Mais j'ai tendance ( dés fois) à  ne pas mettre @class mais plutôt un #import "MyClass.h", est-ce que je mérite une claque image/huh.gif' class='bbc_emoticon' alt='???' /> ?



Autre point, j'ai regardé bien sur dans la documentation Apple à  propos de l'utilisation de @class,



"The @class directive minimizes the amount of code seen by the compiler and linker, and is therefore the simplest way to give a forward declaration of a class name. Being simple, it avoids potential problems that may come with importing files that import still other files. For example, if one class declares a statically typed instance variable of another class, and their two interface files import each other, neither class may compile correctly. "



Est-ce que quelqu'un peux m'expliquer la phrase soulignée SVP ?. Un exemple de code sera le bienvenu. Merci

Réponses

  • Exemple :
    <br />
    @interface Class1 : NSObject<br />
    {<br />
    Class2 * ref1;<br />
    }<br />
    @end<br />
    @interface Class2 : NSObject<br />
    {<br />
    Class1 * ref2;<br />
    }<br />
    @end<br />
    


    Chacune des classes utilise l'autre, il y a une dépendance cyclique. Dans ce cas il vaut mieux qu'au moins une des deux déclarations de classe utilisée soit en @class plutôt que #import.
  • Merci pour té réponse.



    En fait ce que j'ai pas compris : que ce que une redondances cyclique peux engendrer comme erreur ?
  • AliGatorAliGator Membre, Modérateur
    Imagine la situation suivante :
    • Paul tient le pied de Jean (retain). Paul ne lâchera Jean (release) que quand Paul ne sera plus tenu par personne (plus personne qui le retain, donc il est dealloc)
    • Jean tient le pied de Paul (retain). Jean ne lâchera Paul (release) que dans son dealloc également, donc que quand Jean ne sera plus retenu par personne


    Il y a un retain cycle car Paul retient Jean et Jean retient Paul



    Ca pose problème parce que du coup personne ne lâchera jamais l'autre : Paul va attendre que Jean le lâche pour lâcher Jean lui-même, et Jean va faire de même, au final personne ne cèdera, personne ne lâchera prise, et Jean et Paul ne pourront jamais se libérer (dealloc), donc il y a fuite mémoire car tes objets ne sont jamais détruits.



    Si Paul tient le pied de Jean (retain / strong reference), mais que Jean ne fait que regarder Paul du retard (weak reference), alors on évite le "retain cycle" et Paul lâchera le pied de Jean dès que plus personne ne retiendra Paul lui-même. Et comme Jean ne retient pas Paul mais ne fait que le regarder de loin (weak reference) cela ne va pas empêcher Paul de lâcher prise.
  • Ah bon, ça pose un problème ? Pourtant les Toons utilisent souvent la technique du double retain pour voler dans l'air, traverser un précipice, rester accroché au mur par un pinceau, etc ..
  • AliGatorAliGator Membre, Modérateur
    Bah oui c'est pour ça qu'ils restent en vie au lieu d'être détruits, comme les objects en Objective-C en fait image/biggrin.png' class='bbc_emoticon' alt=':D' />
  • Ah d'accord ..
  • CéroceCéroce Membre, Modérateur
    @Ali: tu es hors sujet... (oui, tu as confondu avec l'autre message).
  • CéroceCéroce Membre, Modérateur
    février 2013 modifié #9
    @Samir:



    Les dépendances cycliques posent un problème logique, que les compilateurs ne savent pas résoudre, il me semble: Classe1 référence Classe2 qui référence Classe1 qui références Classe 2, etc. Tu as donc une boucle de références.

    En utilisant le mot clé @class, tu signifies seulement au compilateur qu'il existe une telle classe, mais pas comment elle est faite exactement. Problème résolu!



    Je dirais qu'en général, c'est de toute façon une mauvaise idée, parce que tu crées une dépendance forte entre les deux classes. Il est souvent plus intéressant de dire qu'on pointe sur un objet qui se conforme à  un protocole, et pas plus.
  • AliGatorAliGator Membre, Modérateur
    'Céroce' a écrit:
    @Ali: tu es hors sujet... (oui, tu as confondu avec l'autre message).
    Ah merde oui j'ai vu redondance cyclique j'ai pensé à  retain cycle et pas à  inclusions cycliques de #import image/wink.png' class='bbc_emoticon' alt=';)' />



    Au temps pour moi
  • Cela dit j'ai bien lu que ça pose problème mais je n'ai absolument pas compris pourquoi. Autant les cycles de références fortes c'est clair, autant je suis largué avec les cycles d'imports :

    - ça ne devrait pas poser problème au compilo puisque de base un #import ne peut être inclus qu'une fois, contrairement au #include

    - ou alors un problème d'édition de liens, ou de dynamic loader ?
  • CéroceCéroce Membre, Modérateur
    Moi non plus, je suis incapable de dire pourquoi. Peut-être est-ce dû aux compilateurs en deux passes ?
  • AliGatorAliGator Membre, Modérateur
    février 2013 modifié #13
    Pour moi en fait les forward declaration (@class, etc) ne sont pas faites pour éviter les imports cycliques.

    #import fait ça très bien.



    @class c'est juste pour dire au compilateur "il y a une classe avec ce nom là , tu n'as pas besoin de savoir de quoi elle est constituée, je te dis juste qu'elle existe". Avantage, ça évite au compilateur d'inclure tout le contenu du .h juste pour compiler alors qu'il n'en a pas besoin d'une part, et ça évite que le compilateur recompile le fichier si le .h a changé (par exemple si on a changé une méthode) alors que le fichier qui l'utilise s'en fiche des méthodes en question.



    Par exemple, si dans A.h je veux déclarer une propriété de type B :
    • Soit je #import "B.h" et dans ce cas lors de la compilation, tout le contenu de "B.h" sera inclus avant "A.h" pour que le compilateur compile toute l'unité. Mais c'est dommage d'inclure tout le contenu de "A.h" alors qu'on ne manipule pas l'objet A (on accède pas à  ses propriétés, son s'en fiche de quoi il hérite... on a juste besoin de savoir que la classe existe), et ça va rallonger le temps de compilation car il aura un fichier plus gros à  compiler (pour rien). Et si B.h est modifié plus tard, au prochain build le compilo va recompiler A.h aussi, voyant que A.h importe B.h et que la modification de B.h peut donc avoir des effets sur A.h... donc encore du temps de compilation plus long
    • Soit j'écris juste @class B et dans ce cas pour A.h ça suffit car on a juste besoin de savoir que A existe, que ce soit une sous-classe de NSObject, de UIView ou de NSString, ou qu'il déclare telle ou telle méthode on s'en fiche pour juste déclarer une "@property B* b" dans A.h. Du coup à  la compilation ça compile plus vite et ça ne sera pas recompilé même si B.h change.


    Bref, pour moi ces forward declarations c'est pas pour éviter les imports cycliques (ça c'est le rôle de #import, fonctionnalité que n'a pas #include), mais pour réduire les dépendances entre les .h, éviter de faire des unités de compilations trop grosses, et améliorer les temps de compilation.





    Et bien sûr, ça sert également si on ne sépare pas dans des headers différents les classes ou protocoles A et B (cas courant du "@protocol TotoDelegate" qui déclare des méthodes genre "-(void)toto:(Toto*)toto didTellJoke:(NSString*)joke", mais qui est déclaré dans le même Toto.h, avant la déclaration de la classe Toto elle-même... )
Connectez-vous ou Inscrivez-vous pour répondre.