Objet immutable en public, mutable en privé

CéroceCéroce Membre, Modérateur
février 2013 modifié dans Objective-C, Swift, C, C++ #1
Je souhaiterais que ma classe ait ceci dans son interface publique (.h):


@property (strong, readonly) NSArray *places;<br />
- (void) addPlace:(Place *)place;<br />
- (void) removePlaceAtIndex:(NSUInteger)placeIndex;




Par contre, en interne, je veux que places soit un NSMutableArray. Vous comprenez l'idée: l'utilisateur de la classe ne doit pas pouvoir modifier lui-même places, mais passer par les méthodes add et remove.



Comment feriez-vous cela ? Pour l'instant, j'ai un NSMutableArray déclaré dans une catégorie anonyme du .m:
@property (strong) NSMutableArray *placesInternal;




Et je le renvoie ainsi:
- (NSArray *) places<br />
{<br />
return placesInternal;<br />
}


(C'est bien ce getter -places qui apparaà®t dans le .h)



Auriez-vous une meilleure idée ?
«1

Réponses

  • Hello,



    .h

    @property (strong, readonly) NSArray *places;

    et dans le .m

    @property (strong, readonly) NSMutableArray *places;
  • CéroceCéroce Membre, Modérateur
    ça ne fonctionne pas. On ne peut pas donner le même nom à  deux propriétés de classes différentes.
  • 'Céroce' a écrit:


    Je souhaiterais que ma classe ait ceci dans son interface publique (.h):


    @property (strong, readonly) NSArray *places;<br />
    - (void) addPlace:(Place *)place;<br />
    - (void) removePlaceAtIndex:(NSUInteger)placeIndex;
    




    Par contre, en interne, je veux que places soit un NSMutableArray. Vous comprenez l'idée: l'utilisateur de la classe ne doit pas pouvoir modifier lui-même places, mais passer par les méthodes add et remove.



    Comment feriez-vous cela ? Pour l'instant, j'ai un NSMutableArray déclaré dans une catégorie anonyme du .m:
    @property (strong) NSMutableArray *placesInternal;
    




    Et je le renvoie ainsi:
    - (NSArray *) places<br />
    {<br />
    return placesInternal;<br />
    }
    


    (C'est bien ce getter -places qui apparaà®t dans le .h)



    Auriez-vous une meilleure idée ?




    Bonjour,
    <br />
    - (NSArray *) places<br />
    {<br />
    return placesInternal;<br />
    }<br />
    




    Au lieu de lui retourner placesInternal, tu crée un nouveau NSArray que tu remplis avec les données de ton NSMutableArray



    puis tu le retournes.
  • CéroceCéroce Membre, Modérateur
    Non, ce n'est pas nécessaire, je considère que déclarer places en NSArray* est une protection suffisante.

    Une fois places récupéré, le code client pourrait effectivement appeler les méthodes de NSMutableArray pour ajouter ou retirer des éléments, mais il faudrait caster places en NSMutableArray pour cela.
  • AliGatorAliGator Membre, Modérateur
    Oui moi c'est comme ça que je fais. J'implémente le getter de places et retourne le NSMutableArray, implicitement casté en NSArray
  • CéroceCéroce Membre, Modérateur
    L'idée m'est venue de regarder dans Les Design Patterns de Cocoa, et c'est malheureusement ainsi qu'ils procèdent. Ils utilisent une variable d'instance mutablePlaces et la renvoient dans -(NSArrayPlaces).



    Dommage, je pensais qu'il y aurait une manière plus élégante de faire.
  • MalaMala Membre, Modérateur
    Genre dans le .h...
    ...<br />
    <br />
    [color=#7700AC][font=Menlo][size=2][color=#000000]	[/color]NSMutableDictionary[color=#000000] *[/color][color=#458287]toto[/color][color=#000000];[/color][/size][/font][/color]<br />
    [font=Menlo][size=2]}[/size][/font]<br />
    <br />
    [font=Menlo][size=2][color=#c600a3]@property[/color]([color=#c600a3]readonly[/color]) NSDictionary *toto;[/size][/font]<br />
    




    et dans le .m...
    [color=#7700AC][font=Menlo][size=2][color=#000000]- ([/color]NSDictionary[color=#000000]*)toto[/color][/size][/font][/color]<br />
    [font=Menlo][size=2]{[/size][/font]<br />
    [color=#450084][font=Menlo][size=2][color=#000000]	[/color][color=#c600a3]return[/color][color=#000000] [[[/color][color=#458287]toto[/color][color=#000000] [/color]mutableCopy[color=#000000]] [/color]autorelease[color=#000000]];[/color][/size][/font][/color]<br />
    [font=Menlo][size=2]}[/size][/font]<br />
    




    Je vois pas le problème. A moins que je n'ai pas bien compris la question. image/baby.gif' class='bbc_emoticon' alt=' :o ' />
  • CéroceCéroce Membre, Modérateur
    C'est essentiellement un problème de syntaxe.

    On ne peut pas déclarer la propriété comme ça dans le .h:


    <br />
    @property (strong, readonly) NSArray *places;<br />
    
  • CeetixCeetix Membre
    février 2013 modifié #10
    Si tu mets ça dans ton .h et que dans le .m tu déclares ton interface avant l'implémentation et que tu enlèves le readonly ? Au final vu que tu es en readonly tu t'en fiches que ce soit mutable aux yeux de l'user non ?



    .h
    @property (strong, readonly) NSMutableArray *places;<br />
    




    .m
    @interface TaClasse ()<br />
    @property (strong) NSMutableArray *places;<br />
    @end<br />
    @implementation TaClasse<br />
    
  • MalaMala Membre, Modérateur
    février 2013 modifié #11
    Le problème est lié à  ARC?
  • MalaMala Membre, Modérateur
    février 2013 modifié #12
    'Ceetix' a écrit:


    Si tu mets ça dans ton .h et que dans le .m tu déclares ton interface avant l'implémentation et que tu enlèves le readonly ? Au final vu que tu es en readonly tu t'en fiches que ce soit mutable aux yeux de l'user non ?


    Dans ce cas l'utilisateur de la classe a tout loisir de pourrir le contenu du tableau.
  • Arf oui je suis con image/sleep.png' class='bbc_emoticon' alt='-_-' /> Du coup la solution d'Ali est la meilleure.
  • AliGatorAliGator Membre, Modérateur
    février 2013 modifié #14
    'Mala' a écrit:
    et dans le .m...
    - (NSDictionary*)toto<br />
    {<br />
    	return [[toto mutableCopy] autorelease];<br />
    }<br />
    




    Je vois pas le problème. A moins que je n'ai pas bien compris la question. image/baby.gif' class='bbc_emoticon' alt=' :o ' />
    Je crois que tu as un peu mélangé les pinceaux ^^ Tu crées une mutableCopy d'un truc déjà  mutable, pour le retourné en tant qu'objet immutable... image/tongue.png' class='bbc_emoticon' alt=':P' />
  • AliGatorAliGator Membre, Modérateur
    'Céroce' a écrit:


    On ne peut pas déclarer la propriété comme ça dans le .h:


    <br />
    @property (strong, readonly) NSArray *places;<br />
    

    Bah si, bien sûr qu'on peut ;-) Mais faut juste coder le getter soi-même pour que l'accès à  cette propriété retourne le NSMutableArray interne (cf ma solution)
  • AliGatorAliGator Membre, Modérateur
    // Dans le .h<br />
    @interface TotoClass<br />
    @property(readonly) NSArray* places;<br />
    @end<br />
    <br />
    // Dans le .m<br />
    @interface TotoClass()<br />
    @property(strong) NSMutableArray* mutPlaces<br />
    @end<br />
    @implementation TotoClass<br />
    -(NSArray*)places {<br />
      return (NSArray*)mutPlaces;<br />
    }<br />
    @end
    
  • Mouais, pas très plaisant de retourner directement ton mutPlaces en le castant en NSArray...

    ça n'empêchera pas l'objet qui demande [toto places] d'agir sur la array à  coup de addObject: et cie. Même si le compilo va râler, il pourra image/smile.png' class='bbc_emoticon' alt=':)' />



    J'ai l'habitude de réécrire le getter comme ceci:
    <br />
    - (NSArray *)places{<br />
    return [NSArray arrayWithArray:_mutPlaces];<br />
    }<br />
    
  • AliGatorAliGator Membre, Modérateur
    'ldesroziers' a écrit:


    Mouais, pas très plaisant de retourner directement ton mutPlaces en le castant en NSArray...

    ça n'empêchera pas l'objet qui demande [toto places] d'agir sur la array à  coup de addObject: et cie. Même si le compilo va râler, il pourra image/smile.png' class='bbc_emoticon' alt=':)' />
    Y'en a un qui n'a pas tout lu de la discussion...
    'Céroce' a écrit:


    Non, ce n'est pas nécessaire, je considère que déclarer places en NSArray* est une protection suffisante.

    Une fois places récupéré, le code client pourrait effectivement appeler les méthodes de NSMutableArray pour ajouter ou retirer des éléments, mais il faudrait caster places en NSMutableArray pour cela.
  • zoczoc Membre
    février 2013 modifié #19
    'ldesroziers' a écrit:


    Mouais, pas très plaisant de retourner directement ton mutPlaces en le castant en NSArray...

    ça n'empêchera pas l'objet qui demande [toto places] d'agir sur la array à  coup de addObject: et cie. Même si le compilo va râler, il pourra image/smile.png' class='bbc_emoticon' alt=':)' />



    J'ai l'habitude de réécrire le getter comme ceci:
    <br />
    - (NSArray *)places{<br />
    return [NSArray arrayWithArray:_mutPlaces];<br />
    }<br />
    



    Pourquoi pas très plaisant ? NSMutableArray dérive de NSArray non ? Donc l'utilisation du cast me semble logique, c'est une des bases de la POO...



    Et en terme de perf c'est pas forcément super intelligent, parce que selon le nombre d'éléments dans _mutPlaces, tu vas faire des dizaines, centaines, milliers de retain pour rien en pratique (puis de release quand le tableau va être détruit), puisque dans la majorité des cas la solution du cast en NSArray est satisfaisante.



    Après, si l'utilisateur de la méthode fait n'importe quoi, tu n'y peux rien. A un moment, il faut faire un compromis et arrêter de vouloir tout blinder, parce que cela à  un impact sur les performances.
  • 'Mala' a écrit:

    [color=#7700AC][font=Menlo][size=2][color=#000000]- ([/color]NSDictionary[color=#000000]*)toto[/color][/size][/font][/color]<br />
    [font=Menlo][size=2]{[/size][/font]<br />
    [color=#450084][font=Menlo][size=2][color=#000000]	[/color][color=#c600a3]return[/color][color=#000000] [[[/color][color=#458287]toto[/color][color=#000000] [/color]mutableCopy[color=#000000]] [/color]autorelease[color=#000000]];[/color][/size][/font][/color]<br />
    [font=Menlo][size=2]}[/size][/font]<br />
    




    Je vois pas le problème. A moins que je n'ai pas bien compris la question. image/baby.gif' class='bbc_emoticon' alt=' :o ' />


    A part fait tout un tas de retain, puis de release inutiles, qui ont un coût, il n'y a pas de problèmes, effectivement.
  • février 2013 modifié #21
    'zoc' a écrit:


    Après, si l'utilisateur de la méthode fait n'importe quoi, tu n'y peux rien. A un moment, il faut faire un compromis et arrêter de vouloir tout blinder, parce que cela à  un impact sur les performances.




    Oui ça dépend des cas d'utilisation effectivement. Mais si ce n'est censé être appelé très souvent je trouve ça mieux d'utiliser ce genre de protection.



    J'ai souvent été tenté d'utiliser cette sur-protection de simple "peur" qu'on me dise un jour qu'un cast ne suffit pas car, justement, du potentiel risque que la personne derrière s'amuse avec l'objet mutable malgré qu'on ait casté l'objet comme étant immuable. Mais c'est vrai qu'à  partir de là .. c'est plus vraiment ma faute...
  • AliGatorAliGator Membre, Modérateur
    Tiens ceci dit faudrait faire des tests, car je sais qu'Apple a méga-optimisé ce genre de choses. Par exemple :
    • Un "copy" sur une NSString ou un NSArray ou tout objet immutable fait en fait sous le capot un retain, puisque l'objet d'origine n'est pas modifiable et que sa copie non plus. Ce qui évite de faire une allocation mémoire d'une nouvelle instance pour rien
    • Les NSMutableArray et NSArray sont sous le capot la même classe, avec juste un flag disant s'ils sont mutable ou pas. C'est d'ailleurs pour ça que quand on appelle des méthodes comme "addObject" sur un NSArray, on a pas une exception "unrecognized selector sent to instance" disant que la méthode "addObject" n'existe pas pour la classe "NSArray", mais on a une exception "mutable method sent to immutable instance" (ce qui montre que la classe NSArray a conscience que cette méthode existe sur sa variante mutable)


    Du coup vu ce genre d'optimisations que fait Apple sur le sujet, j'en suis à  me demander s'il n'y aurait pas un moyen de "convertir un NSMutableArray en NSArray" sans créer de nouvelle instance, juste avec une méthode qui changerait le flag indiquant la "mutability" ou une astuce comme ça... Ce qui résoudrait les 2 problèmes à  la fois. Enfin peut-être pas aller jusqu'à  ce point là  non plus (car si on faisait ça le tableau interne serait aussi rendu immutable, ce qui va poser problème), mais peut-être imaginer que +[NSArray arrayWithArray:] fait du Copy-On-Write ou ce genre de chose, ce qui rendrait l'overhead de créer une nouvelle instance de NSArray bien moins violente, car il n'y aurait alors pas à  envoyer de retain à  tous les objets du tableau y compris si le tableau contenait un nombre important d'objets...



    De plus, le problème de retourner directement le NSMutableArray juste casté en NSArray, c'est que le NS(Mutable)Array peut ensuite changer par la suite (via des méthodes internes à  ta classe) et tu auras alors des problèmes. Par exemple imagine que tu t'amuses à  faire une boucle sur tes places pour ajouter une copie de chacune dans ton tableau (c'est pour l'exemple hein, on sait pas trop pourquoi image/tongue.png' class='bbc_emoticon' alt=':P' />) :
    NSArray* originalPlaces = obj.places<br />
    for(Place* p in originalPlaces) {<br />
      Place* pc = [p copy];<br />
      [obj addPlace:pc];<br />
    }
    
    Là  le problème c'est que contrairement à  ce qu'on pense faire en lisant le code, à  savoir récupérer le tableau de places à  un instance T, puis parcourir ce tableau pour ajouter des places... bah si tu ne fais qu'un cast dans le getter de "-places", originalPlaces va pointer sur ton NSMutableArray interne. Et au fur et à  mesure que tu vas parcourir ta boucle, ce tableau va changer. Au final ta boucle ne s'arrêtera sans doute jamais...



    Autre exemple, je veux écrire un test unitaire pour ta méthode, ou encore vérifier si ma place a bien été ajoutée (ou si par exemple elle n'a pas été ajoutée parce que addPlace n'ajoute pas la place si elle existe déjà , imaginons, et que je veux savoir si ça a été le cas) :
    NSArray* placesBefore = obj.places<br />
    [obj addPlace:p];<br />
    NSArray* placesAfter = obj.places;<br />
    STAssertEquals(placesAfter.count+1,placesBefore.count,@&quot;The place was not added&quot;)
    
    Bien que quand je lis le code comme ça, rien ne me choque, en pratique ce TestU va toujours foirer, car placesBefore et placesAfter seront exactement la même instance, contrairement à  ce que les conventions de nommage laissent penser.







    En conclusion, si je suis d'accord qu'il faut éviter d'allouer des instances pour rien quand on peut éviter, en pratique je pense que (surtout si tu actives ARC, en plus) à  la fois le compilateur et la façon dont Apple a codé Cocoa et les NSArray, l'overhead ne doit pas être si grand voire doit être fortement réduite par les optimisations, et qu'en contrepartie essayer d'optimiser toi-même risque plus de t'apporter des mauvaises surprises comme celles mentionnées ci-dessus plutôt que de t'apporter des bénéfices. A ce stade je préfère une API sans ambiguité et éviter à  celui qui utilisera ma classe de perdre des heures à  essayer de débuguer un truc tout ça parce que l'API et les conventions de nommage lui laissent penser un truc qui n'est en pratique pas respecté, plutôt que d'essayer de gagner quelques ms voire ns qui en plus seront déjà  réduites par les optimisations du compilateur et de ARC...
  • CéroceCéroce Membre, Modérateur
    'AliGator' a écrit:

    NSArray* originalPlaces = obj.places<br />
    for(Place* p in originalPlaces) {<br />
      Place* pc = [p copy];<br />
      [obj addPlace:pc];<br />
    }
    



    Tiens, je n'avais pas songé à  cet exemple.

    Mais, je crois qu'on arrive là  à  une limite bien connue de la POO, qui fait que l'abstraction finit par poser problème.



    En tout cas, je suis étonné que ma question bien anodine ait fait autant parlé!
  • Dans le livre que je viens de lire, au chapitre "Déclarer une propriété avec un getter public et un setter privé" l'auteur conseil d'écrire:

    dans le machin.h
    @interface machin : NSObject <MonProtocole,NSCopying> {

    @private

    NSString *maChaine ;

    }

    @property (nonatomic, readonly, assign) NSString *maChaine ;

    @end
    Et dans le machin.m
    //extension privé de machin:

    @interface machin( )

    @property (readwrite, assign, nonatomic) NSString *maChaine ;

    @end



    #import "machin.h"

    @implementation machin

    @synthesize maChaine

    @end
    cela vous parait correcte?
  • MalaMala Membre, Modérateur
    'AliGator' a écrit:


    Je crois que tu as un peu mélangé les pinceaux ^^ Tu crées une mutableCopy d'un truc déjà  mutable, pour le retourné en tant qu'objet immutable... image/tongue.png' class='bbc_emoticon' alt=':P' />


    Oups pardon, tu as raison! C'est ma tournée pour la peine! image/cliccool.gif' class='bbc_emoticon' alt=' :p ' /> image/eddy58.gif' class='bbc_emoticon' alt=' :p ' /> image/eddy58.gif' class='bbc_emoticon' alt=' :p ' />
  • MalaMala Membre, Modérateur
    'zoc' a écrit:


    A part fait tout un tas de retain, puis de release inutiles, qui ont un coût, il n'y a pas de problèmes, effectivement.


    Autant je suis pas hyper fan de certaines guidelines à  l'emporte pièce d'Apple, autant je trouve ça tout à  fait louable ici.



    Et comme le sous-entend Ali, s'il existe des classes non mutables, c'est pas pour le plaisir du style. Il y a des raisons sous-jacentes d'optimisation de la part d'Apple.



    Bref, oui ça a un coût sur le retour de méthode mais de là  à  dire que c'est inutile...
  • AliGatorAliGator Membre, Modérateur
    février 2013 modifié #27
    "tablier" a écrit:
    Dans le livre que je viens de lire, au chapitre "Déclarer une propriété avec un getter public et un setter privé" l'auteur conseil d'écrire:

    dans le machin.h

    ...

    Et dans le machin.m

    ...

    cela vous parait correcte?
    Alors :
    • Cela n'est pas tout à  fait la même chose que ce qui est demandé par Céroce. Céroce demande comment déclarer une propriété qui a un certain type en public (NSArray) et qui est typé différement, avec une sous-classe (NSMutableArray), en privé. Et pas une déclaration readonly en public et readwrite en privé, ça on sait faire.

      Il lui faut en privé un NSMutableArray car il lui faut pouvoir modifier le contenu du tableau, pour y ajouter ou supprimer des éléments.

      Le NSMutableArray lui peut tout à  fait rester en readonly, même en private, car on ne va allouer l'instance qu'une seule fois, c'est son contenu qu'on va modifier. Pas besoin qu'il soit readwrite pour ça. C'est donc un problème différent
    • Le code que tu cites de ton livre est bon mais bien trop complet :
      • Il n'y a jamais eu besoin de déclarer les variables d'instance en iOS (ni en OSX 64 bits, seulement en OSX 32 bits), et je déconseille fortement de déclarer explicitement vos variables d'instance (sauf cas particulier), car cela peut mener à  confusion entre la propriété et son ivar, ce qui mène au final à  des erreurs de gestion mémoire. Donc à  éviter pour pas risquer de s'emmêler les pinceaux
      • De plus depuis les dernières versions du compilateur il n'y a pas besoin non plus de déclarer les @synthesize
      • Enfin les #import doivent toujours être au début de fichier, et pas au milieu (même si dans certains cas ça peut finir par donner du code c'est valable, c'est pas fait pour)





    Donc il suffit juste d'écrire dans Machin.h :
    @interface Machin : NSObject &lt;monprotocole,nscopying&gt;<br />
    @property (nonatomic, assign, readonly) NSString *maChaine ;<br />
    @end
    


    Et dans le Machin.m :
    #import &quot;Machin.h&quot;<br />
    <br />
    @interface Machin ( )<br />
    @property (nonatomic, assign, readwrite) NSString *maChaine ;<br />
    @end<br />
    <br />
    @implementation Machin<br />
    - (void)test {<br />
      self.maChaine = ...<br />
    }<br />
    @end
    
    Pas besoin de synthesize, ni de ivar, etc... juste redéfinir la @property qu'on a mis en readonly dans le .h en readwrite dans le .m, c'est tout.



    Mais bon, comme je le disais plus haut ça ne répond pas à  la problématique de Céroce qui est un peu différente...
  • CéroceCéroce Membre, Modérateur
    'tablier' a écrit:


    cela vous parait correct?


    Une NSString en assign, c'est forcément incorrect.
  • AliGatorAliGator Membre, Modérateur
    'Céroce' a écrit:


    Une NSString en assign, c'est forcément incorrect.
    Ah oui tiens j'avais même pas vu cette boulette de l'exemple cité ! En effet !



    Décidément, à  part le faire de redéfinir la property(readonly) en (readwrite), qui est finalement le seul truc à  faire, tout le reste de l'exemple n'est pas vraiment très clean...
  • Une NSString en assign, c'est forcément incorrect.
    L'objet devrait être mutable?

    Ok, ne pas suivre les exemples du bouquin: Le guide de survie Objective-C 2.0 publié en 2010. (pearson)

    Question subsidiaire:
    Il n'y a jamais eu besoin de déclarer les variables d'instance en iOS en OSX 64 bits, seulement en OSX 32 bits
    Si je programme "à  l'ancienne", sans properties et en déclarant normalement mes variables, à  part le fait que certaines vérifications ne seront pas faites, quel sera vraiment le risque?
  • AliGatorAliGator Membre, Modérateur
    février 2013 modifié #31
    'tablier' a écrit:


    L'objet devrait être mutable?
    Heiiinn ? Mais quel rapport entre assign/retain/weak/strong d'un côté et mutable ou pas de l'autre ? Les deux n'ont rien à  voir ! Tu veux dire que tu ne comprends pas pourquoi un objet comme NSString mis en "assign" est un problème ? Tu me fais peur là ...

    Si tu ne vois pas le souci, va très très vite réviser les bases de la gestion mémoire en Objective-C (que ce soit avec ou sans ARC d'ailleurs, le pb est le même), en particulier la différence entre assign ou weak et retain/strong !!!


    'tablier' a écrit:
    Si je programme "à  l'ancienne", sans properties et en déclarant normalement mes variables, à  part le fait que certaines vérifications ne seront pas faites, quel sera vraiment le risque?
    La liste est longue, mais pour résumer tu risques :
    • D'oublier de faire des release d'anciennes valeurs, donc d'avoir des fuites mémoire
    • D'oublier de faire des retain de nouvelles valeurs, donc d'avoir des crash (type EXC_BADACESS)
    • De ne pas déclencher le KVO, donc d'avoir plusieurs mécanismes qui ne marcheront pas, genre le NSNotificationCenter, les bindings OSX, etc
    • D'avoir des problèmes de multi-threading lorsque tu as des accès MT à  des ressources partagées, et donc des "racing conditions" qui font que dans certains cas ton programme va soit planter soit retourner des résultats incohérents, dûs à  ces accès concurrents non protégés
    • Et j'en passe




    PS : Pourquoi as-tu barré "iOS" dans ma citation précédente ? Je réitère ce que j'ai dit, depuis le début on a jamais eu besoin de déclarer les variables d'instance sous iOS. Jamais. Ceci est vrai depuis le "Modern Runtime", mais le Legacy Runtime qui existait avant n'était utilisé que par OSX 32 bits avant la sortie de iOS. Quand iOS est sorti, dès sa première version, il tournait déjà  avec le Modern Runtime. (Et quand ils ont sorti OSX 64 bits, il est aussi passé au Modern Runtime). Ce qui fait que même depuis les tout débuts d'iOS, on n'a vraiment jamais eu besoin de déclarer les variables d'instance correspondantes aux backing variables des @property, dès qu'on écrivait une @property et un @synthesize, la variable d'instance était générée automatiquement. (Et depuis peu avec les dernières versions du compilateur y'a mm plus besoin de @synthesize, mais ça par contre c'est assez récent)
Connectez-vous ou Inscrivez-vous pour répondre.