[Résolu] modifier une ivar

Paul_pPaul_p Membre
juillet 2011 modifié dans API UIKit #1
Salut,

je ne suis pas sûr de bien comprendre quand on utilise le "self." pour changer une property, et pourquoi dans le même code on envoie un message à  cette même "ivar" mais cette fois sans changer sa property. Exemple avec locationMeasurements :

- (void)viewDidLoad {<br />&nbsp; &nbsp; self.locationMeasurements = [NSMutableArray array];<br />}<br /><br />- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {<br />&nbsp; &nbsp; [locationMeasurements addObject:newLocation];<br /><br />- (void)viewDidUnload {<br />&nbsp; &nbsp; locationDetailViewController = nil;<br />}<br /><br /><br />- (void)reset {<br />&nbsp; &nbsp; [self.locationMeasurements removeAllObjects];<br />}


Donc, si on a besoin de locationMeasurements en dehors de la classe, pourquoi est-ce que l'on envoie ici le message "addObject" directement à  l'ivar, et non plutôt à  la property (qui ferait coup double et changerait aussi l'ivar) ?

Merci de votre réponse

Réponses

  • DrakenDraken Membre
    08:59 modifié #2
    A moins d'une raison technique qui m'échappe, l'auteur a dus faire une erreur d'inattention. Cela fonctionne mais le code n'est pas propre. Il est préférable de déclarer uniquement les propriétés, sans créer de variables d'instances correspondantes, afin d'éviter ce genre de pratiques.

  • juillet 2011 modifié #3
    Mais...
    Bon déjà  le seul truc choquant c'est
    <br />- (void)viewDidUnload {<br />&nbsp; &nbsp; locationDetailViewController = nil;<br />}<br />
    


    à  remplacer par self.locationDetailViewController = nil; ou [locationDetailViewController release], locationDetailViewController = nil;
    Sauf si celui-ci est en type "assign" (dans ce cas le code est bon) mais c'est quand même zarb comme façon de procéder et pas très sécure je trouve.

    Sinon pour
    <br />- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {<br />&nbsp; &nbsp; [locationMeasurements addObject:newLocation];<br />}<br />
    


    Je ne vois pas du tout le problème.
    self.locationMeasurements = [NSMutableArray array]; revient à  faire:
    <br />- (void)setLocationMeasurements:(NSMutableArray*)aMutableArray<br />{<br />@synchronized(self){<br />	if(locationMeasurements!=aMutableArray<br />	{<br />		[self willChangeValueForKey:@&quot;locationMeasurements&quot;];<br />		locationMeasurements release];<br />		locationMeasurements = [aMutableArray retain];<br />		[self didChangeValueForKey:@&quot;locationMeasurements&quot;];<br />	}<br />}<br />}<br />
    


    [locationMeasurements addObject:newLocation] ou [self.locationMeasurements addObject:newLocation]; revient à  faire la même chose. Sauf que dans le premier cas tu accès direct à  la iVar, et dans le second tu passes par une méthode (que tu ne vois pas):
    <br />- (NSMutableArray*)locationMeasurements<br />{<br />	@synchronized(self)<br />	{<br />		return [[locationMeasurements retain] autorelease];<br />	}<br />}<br />
    


    (Je pars du principe que tout est en property nonatomic, retain.

    Aussi, une erreur que je vois c'est que locationMeasurements est instancié dans viewDidLoad. Donc par précaution et par soucis de gestion mémoire convenable:
    <br />- (void)viewDidUnload<br />{<br />	self.locationMeasurements=nil; // self.... = pour le KVO<br />}<br />
    



    Il faut arrêter de penser à  accéder à  une variable uniquement en utilisant self.quelquechose.
    Tant que ce sont vos ivars et que vous vous trouvez dans 'self', pas besoin d'utiliser l'accesseur self.quelquechose. Chose qui ralenti (très légèrement certes) l'exécution du code.

    Tu parles aussi de "changer de property". Or ça n'est pas du tout le cas via -addObject:. Certes tu modifies le contenu de ta array, mais tu ne changes pas son adresse mémoire.
  • laudemalaudema Membre
    08:59 modifié #4
    dans 1311852761:


    Il faut arrêter de penser à  accéder à  une variable uniquement en utilisant self.quelquechose.

    Pour que le changement soit reflété dans l'interface utilisateur ça reste plus rapide que d'écrire
    <br />[self willChangeValueForKey:@&quot;quelqueChose&quot;]<br />quelquechose = newQuelquechose;<br />[self didChangeValueForkey:@&quot;quelqueChose&quot;]<br />[code]
    
  • 08:59 modifié #5
    dans 1311854962:

    dans 1311852761:


    Il faut arrêter de penser à  accéder à  une variable uniquement en utilisant self.quelquechose.

    Pour que le changement soit reflété dans l'interface utilisateur ça reste plus rapide que d'écrire
    <br />[self willChangeValueForKey:@&quot;quelqueChose&quot;]<br />quelquechose = newQuelquechose;<br />[self didChangeValueForkey:@&quot;quelqueChose&quot;]<br />[code]
    



    Je suis d'accord, mais dans le cas de [self.maArray addObject:...]; c'est totalement inutile. Tu noteras que j'ai parlé d'accéder à  une variable, et non la "modifier" (dans le sens release ou ré-instanciation).
  • Paul_pPaul_p Membre
    08:59 modifié #6
    Ok, alors pour résumer, l'encapsulation c'est pouvoir accéder et modifier une ivar d'une classe, depuis l'extérieur de la classe.
    et quand on est à  l'intérieur de cette même classe, on utilise un setter pour les histoires de "mémoire", en faisant un release et retain sur la nouvelle valeur, pour modifier l'ivar.

    Faire : [locationMeasurements addObject:newLocation]; ajoute un élément au tableau, mais c'est aussi faire une opération différente de celle d'un setter, donc pas besoin de toucher aux accesseurs. C'est surtout ce addObject qui me brouille un peu...



  • 08:59 modifié #7
    Je crois que tu te mélanges les pinceaux.
    En clair,
    <br />self.quelqueChose =..;<br />
    

    revient à  faire:
    <br />[self setQuelqueChose:...];<br />
    


    <br />[self.quelqueChose addObject:...];<br />
    

    revient à  faire:
    <br />[quelqueChose addObject:...];<br />
    



    Et donc je t'avouerai que dans le cas de -addObject: il n'y a pas trop utilité à  faire [self.quelqueChose addObject:...];
    Et comme l'a dit Laudema, on peut très bien faire self.quelqueChose = ... histoire de faire marcher un éventuel mécanisme de KVO.
    Si on décide de se passer des getters/setters, il faudra tout faire à  la main:

    <br />- (void)viewDidLoad<br />{<br />	[super viewDidLoad];<br />	[self willChangeValueForKey:@&quot;quelqueChose&quot;];<br />	quelqueChose = [[QuelqueChose alloc] init];<br />	[self didChangeValueForKey:@&quot;quelqueChose&quot;];<br />	<br />}<br /><br />- (void)viewDidUnload<br />{<br />	[self willChangeValueForKey:@&quot;quelqueChose&quot;];<br />	[quelqueChose release], quelqueChose=nil;<br />	[self didChangeValueForKey:@&quot;quelqueChose&quot;];<br />}<br />
    


    Les propriétés ne sont là  que pour nous simplifier la vie, et nous masquer l'implémentation de getters/setters.
    Au moment où tu fais -addObject: sur locationMeasurements, tu n'es pas forcé d'utiliser le getter (via self.locationMeasurements) à  moins d'avoir une subtilité dans son implémentation.
  • Paul_pPaul_p Membre
    08:59 modifié #8
    ok, donc ajouter un élément à  un tableau n'utilise pas un "setter" mais un "getter"? pourtant on ajoute un item, donc on modifie l'ivar depuis l'implementation.

    J'ai en effet regardé sur les scripts que j'ai, et en effet à  chaque fois pour les tableaux, la variable n'utilise pas "self."...
  • juillet 2011 modifié #9
    C'est pas que "ajouter un élément dans un tableau n'utilise pas de setter mais un getter", c'est que le mec n'a pas vu l'intérêt d'utiliser le getter... Faut avouer que c'est un peu con de faire [[self locationMeasurements] addObject:...]. Ce fait [self.locationMeasurements addObject...];

    Après chacun voit midi à  sa porte. Pour ma part j'évite au maximum les [self.bidule doSomething] sauf quand ça devient obligatoire (ex: pour accéder à  la vue d'un controller tu dois obligatoirement faire [self view] ou self.view. J'évite aussi de prendre l'habitude de faire self.view même si c'est plus rapide et très tentant.. En soit ça change queudal et ça a même des avantages, mais visuellement je trouve ça laid.
  • Paul_pPaul_p Membre
    juillet 2011 modifié #10
    Ok, je comprends maintenant.

    C'était facile... :)

    Certes tu modifies le contenu de ta array, mais tu ne changes pas son adresse mémoire.


    Merci à  toi, Draken tu disais :

    Il est préférable de déclarer uniquement les propriétés, sans créer de variables d'instances correspondantes, afin d'éviter ce genre de pratiques.


    t'es sûr de ça? je suis en train de regarder les sample de la doc, mais je vois à  chaque fois pour le moment une propriété qui correspond à  une variable.
  • laudemalaudema Membre
    08:59 modifié #11
    Pour ne pas déclarer de variable eponyme à  la propriété il faut bénéficier d'un système 64 bits et compiler uniquement pour lui..

    Le pire que j'aie fait ? À mes débuts :
    <br />..<br />anObject.retain;<br />...<br />anObject.release;<br />..<br />
    

    Le compilateur compilait sans se plaindre ...
    Après j'ai lu que c'était MAL ..
    Après j'ai compris que c'était nul... car reposant sur les automatismes de KVC/KVO implémentés par Apple alors que je n'appelais pas une propriété..
  • DrakenDraken Membre
    08:59 modifié #12
    dans 1311882163:


    Merci à  toi, Draken tu disais :

    Il est préférable de déclarer uniquement les propriétés, sans créer de variables d'instances correspondantes, afin d'éviter ce genre de pratiques.


    t'es sûr de ça? je suis en train de regarder les sample de la doc, mais je vois à  chaque fois pour le moment une propriété qui correspond à  une variable.


    Vu que je l'utilises tout le temps, oui. C'est une évolution assez récente du compilateur et les templates ne sont pas spécialement à  jour. Essaye par toi-même avec quelques lignes de code.

  • AliGatorAliGator Membre, Modérateur
    08:59 modifié #13
    Non iOS a toujours utilisé le Modern Runtime, qui a toujours autorisé d'utiliser les @property sans avoir déclaré les ivars associées au préalable, le @synthesize les générant sous le capot pour nous.

    Sous Mac OSX, le Modern Runtime (qui permet donc de se passer de la déclaration des ivars associées aux propriétées) n'est utilisé que sur les versions 64 bits par contre en effet.

    Donc si tu veux faire du code juste pour ton appli iOS, et donc que tu sais que tu utiliseras le Modern Runtime, nul besoin de déclarer la ivar. Si tu crées du code générique que tu comptes réutiliser ou laisser à  la communauté, qui pourrait l'utiliser sous OSX 32 bits, alors déclarer les ivars permet de rendre ton code compatible OSX 32 bits. C'est pour ça qu'on trouve encore bcp de code avec les ivars déclarées.

    Mais je suis d'accord avec Draken :
    • avant je conseillais de nommer les ivars et les @property associées différemment, par exemple [tt]NSString* _maChaine;[/tt] pour la ivar, et [tt]@property NSString* maChaine;[/tt] pour la propriété, et [tt]@synthesize maChaine = _maChaine;[/tt] dans le .m pour associer l'un à  l'autre. Ce qui permettait, surtout pour les débutants qui n'étaient pas à  l'aise pour différencier les deux, d'y voir plus clair et de ne pas confondre l'un et l'autre
    • maintenant je conseille de plus en plus, surtout aux débutants qui codent pour iOS et donc qui sont sûrs d'être sur le Modern Runtime, de ne même plus déclarer la ivar et de ne déclarer que la @property (et la @synthétiser), et donc du coup de toujours utiliser la notation avec [tt]self.maprop[/tt] (pour être sûr d'appeler le getter ou setter). Comme ça pour ceux qui ne sont pas sûrs de ce qu'ils devraient utiliser, au moins en utilisant cette notation pointée "self." et donc appelant le setter/getter, on est sûr de ne pas avoir de risque
    • Après, quand on maà®trise mieux et qu'on comprend bien les subtilités entre les propriétés, les ivars, le KVO, etc, comme moi ou ldesroziers, on peut toujours, si on veut optimiser, utiliser l'ivar directement plutôt que la propriété, en particulier à  la place du getter, quand on sait ce qu'on fait et les conséquences que ça a (en particulier ne pas déclencher le KVO ni de faire d'accès atomique). Mais bon pour ce que ça gagne en optimisation, en général c'est rarement utile en même temps.
  • Paul_pPaul_p Membre
    08:59 modifié #14
    Ok merci pour les précisions !
Connectez-vous ou Inscrivez-vous pour répondre.