[Résolu]Des petites questions...
Cirdo
Membre
Bonjour à tous,
Je suis débutant en Cocoa et j'ai quelque question à vous poser qui me travaille depuis un bout de temps :
- Quel est la différence en entre un NSRect et NSSize ?
- Un cycle de référence forte, c'est je te mords le nez et tu me mords le menton ( si j'ai bien compris) ?
- Comment sais-t-on qu'on doit éviter un cycle de référence forte ?
- Si une variable qui est "weak" donc une référence faible, se retrouve dans une référence forte. Que se passe-t-il ?
- Vous vous servez souvent des blocs ?
- Est-ce que les notifications sont souvent présentes dans vos projets ?
- C'est le quoi le asynchrone (il me semble que c'est une histoire de thread ?)
- Est-ce que si mon programme A modifie le registre EAX du mac avec la valeur "0xCAFEBAB". Puis ensuite mon programme B modifie aussi le registre EAX avec la valeur "0xBABCAFE". Est-ce que mon programme A va lire dans le registre EAX "0xCAFEBAB" ou "0xBABCAFE" ?
Je vous remercie d'avance pour répondre à mes questions (débutante ?)
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
- un NSRect est une structure qui représente un rectangle, elle contient une origine NSPoint et une taille NSSize
- je ne comprends pas trop l'analogie
- on le sait car on doit toujours éviter un cycle de références fortes
- une variable n'est pas faible ou forte, seule une référence est faible ou forte. Si une instance n'est pointée que par des références faibles elle est automatiquement détruite. Une instance n'est vivante que tant qu'elle est pointée par une référence forte
- oui très souvent, cela permet de faire du code plus lisible qu'avec des délégués dans tous les sens
- dans chacune de mes applications j'utilise quelques notifications
- une opération est dite asynchrone lorsque la ligne de code qui en déclenche l'exécution n'attend pas la fin d'exécution de l'opération avant de passer à la ligne de code suivante. Il se produit alors ce que l'on appelle de la programmation concurrente ; plusieurs bouts de code s'exécutent en parallèle. Le thread est l'une des techniques utilisées pour faire de la programmation concurrente. Ce n'est pas la plus utilisée sur iOS et OSX
- en principe A va lire "0xCAFEBAB". A et B ne s'exécutent pas forcément sur le même processeur, et même si c'est le cas OSX se charge de permuter les contextes (l'état des registres) à chaque changement d'exécution ; A et B ne peuvent s'exécuter qu'avec leur propre contexte
Les notifications sont globales à l'application. Par principe même, j'évite donc de les utiliser (global, c'est mal).
Concernant les blocks, je les utilises à toutes les sauces, ça apporte beaucoup en terme de lisibilité, de capture, de logique architecturale.
Moi, je les utilise pour le "observer pattern" sur les propriétés de mes objets de modèle, particulèrement quand j'ai quelques sous-vues différentes qui les veillent.
Pourquoi tu utilises des notifications dans ce cas ? pourquoi pas du KVO ?
l'objet "du" modèle, Joanna !
Aaaarrrgghhh ! Merci ::)
L'usage du féminin et du masculin est toujours compliqué pour les anglophones. L'anglais est beaucoup plus simple sur ce point que le français. Tu t'en sors très bien..
C'est vrai qu'en anglais c'est plus simple. Plus simple qu'en Allemand avec le Lune et la Soleil ...
Et c'est vrai que Johanna-sans-h s'en sors très bien.
Oui, effectivement les anglais ont tendance à confondre les masculins des féminin. Je trouve que Joanna se débrouille très bien
Merci à tous pour vos réponses.
Je ne pensais pas qu'on réponde à ma dernière question.
Donc un Rect est un rectangle, la largeur et la hauteur sont exprimées par un NSSize et les points X et Y sont exprimés par un NSPoint ?
jpimbert, on doit toujours éviter un cycles de références dans une application. Mais pourquoi il y'a le mot clé "stong" dans les property ?
Je devrai plutôt utiliser les délégations ou les notifications ?
Encore merci pour vos réponses
C'est plus facile avec Ultralingua sur l'ordinateur
C'est tout à fait ça
Il ne faut pas éviter les cycles de références, il faut éviter les cycles de références FORTES.
S'il y avait du weak par défaut partout sur toutes les propriétés, il n'y aurait aucun cycle de références fortes, c'est vrai. Mais toutes les propriétés vaudraient toujours nil ! Pour qu'une instance ne soit pas détruite automatiquement il faut qu'elle ait au moins une référence forte.
Donc par défaut les propriétés sont des références fortes, ça marche bien la plupart du temps. C'est au développeur de veiller à définir certaines propriétés en référence faible lorsque par construction un cycle de référence est créé.
Si tu veux demander des trucs du genre :
- je vais faire ce truc là , file moi les infos dont j'ai besoin (comme dans un dataSource de TableView), ou bien
- je vais faire ce truc là , puis-je ?
tu ne peux demander qu'à un objet bien particulier, tu vas donc utiliser la délégation
Si tu veux informer :
- coucou, il s'est passé ça, ou bien
- coucou, je viens de me mettre dans l'état Truc, ou bien
- coucou, un bidule vient de changer, ...
tu peux utiliser soit la délégation si tu veux n'informer qu'un objet bien particulier, soit la notification si tu ne sait pas a priori combien d'objets seront intéressés par ton baratin, ou les deux simultanément si tu es généreux (comme dans beaucoup de classes de Cocoa et CocoaTouch).
En pratique je crée la plupart du temps un délégué dans ce genre de cas. Je ne crée une notification en doublon que si je m'aperçois plus tard que j'en ai besoin.
Pense aussi à l'utilisation d'un bloc de code en paramètre de méthode, ce qui permet d'éviter parfois la lourdeur de la définition d'un délégué.
D'accord. Merci
Alors comment on sait qu'on doit utiliser les références fortes ? Quand on met un property à strong, un cycle de référence forte est créé mais avec quel objet ?
De préférences, les notifications quand on veut avertir plusieurs objet et quand on veut avertir un seule objet, c'est les délégation.
Est-ce qu'on doit toujours utiliser les property des objets ?
Oui on doit toujours utiliser les propriétés des objets (sauf quand on trouve des exceptions valables, mais là il faudra que tu t'expliques avec Aligator )
Une property à strong ne crée pas un cycle de références fortes. ça crée juste une référence forte. Il y a cycle lorsque par exemple l'objet A détient l'objet B en référence forte, et vice-versa l'objet B détient l'objet A en référence forte. Là c'est pas bon, il faut définir l'une des deux références comme faible.
Je m'embrouille un peu les pinceaux. Attends ...
C'est quoi la différence entre un cycle de référence forte et une référence forte ? Un cycle de référence forte c'est quand un objets A détient l'objet B et vice versa ? (Si j'ai bien compris ? )
Oui c'est quand il y a un cycle : A->B->A, ou A->B->C->A, ou etc.
Si toutes les références du cycle sont fortes, c'est un cycle de références fortes. Il suffit d'une référence faible dans la chaà®ne pour briser le cycle de références fortes.
- Quand un objet A a une référence FAIBLE vers un objet B, il ne fait que garder un oeil sur B on va dire, il connait B, mais il ne le retient pas pour autant. Il connait B, mais il ne l'empêche pas de partir et vivre sa vie tout seul, il le connait mais il laisse libre, il ne le retient pas.
- Quand un objet A a une référence FORTE vers un objet B, non seulement il ne connait, mais en plus il le retient. Comme s'il lui mettait une laisse ou une chaà®ne pour l'empêcher de partir.
Du coup, comme l'objet B est retenu par A, tant que l'objet A existe, B existe aussi, il ne peut pas se détruire, il est retenu. Par contre si A est détruit, plus rien ne retient B, et B peut se détruire aussi. Ou si A relâche la référence forte qu'il a vers B (la remet à nil), B est alors libre de partir / se détruire.
Tu peux donc voir une référence forte de A vers B comme une laisse d'un maà®tre vers son chien si tu veux.
Un cycle de références fortes, c'est quand les deux se retiennent mutuellement. Si A retient B, alors B ne pourra pas être détruit tant que A ne sera pas lui-même détruit (ou qu'il ne relâchera pas sa référence forte vers B.). Mais si à la fois A retient B (référence forte)... ET que B retient A (référence forte aussi), alors B ne pourra pas être détruit tant que A ne sera pas détruit... et A ne pourra pas être détruit tant que B ne sera pas détruit... c'est le serpent qui se mord la queue, aucun des deux ne lâchera le morceau et personne ne se détruira.
D'accord, tu m'a bien éclaircie.
Mais quand tu dis que l'objet A retient l'objets B, il exécute des fonctions propres à l'objet B ? C'est le fait que l'objet A retient l'objet B ?
Conceptuellement parlant, en général si sémantiquement il y a un lien de "possession" ou de "parenté" ou de "hiérarchie", alors on fait en sorte que le parent ou possesseur retienne le fils ou objet possédé. C'est le parent qui a en général la référence forte sur le fils, et le fils ne fait qu'avoir une reference faible sur le parent.
Exemple : une voiture est constituée d'un moteur. On va donc plutôt avoir tendance à mettre une "@property(strong) Motor* motor;" dans la classe Car pour qu'une voiture ait une reference forte vers le moteur dont elle est constituée. Si la voiture est détruite et n'existe plus, le moteur sera détruit aussi. Si le moteur est détruit et remplacé par contre ce n'est pas pour autant que la voiture qui le contenait disparaà®tra aussi ; elle changera juste de moteur.
Si ensuite la voiture a besoin de demander à son moteur de démarrer, elle va utiliser la reference (forte) vers son moteur pour accéder à l'objet Motor et appeler sa methode "start" : "[self.motor start];". ça reste le moteur qui sait comment démarrer (c'est la classe moteur qui a le code de la methode "start"), la voiture n'a fait que lui demander de démarrer.
Si jamais le moteur à besoin pour une raison ou une autre de savoir à quelle voiture il appartient, et donc pouvoir, depuis la classe Motor, accéder à la Car à laquelle il appartient, il faut bien prevoir une reference dans la classe Motor qui pointe sur la voiture, sinon tu ne pourras pas accéder à l'objet Car depuis l'objet Motor. Dans ce cas tu peux prevoir une "@property(weak) Car* associatedCar" par exemple. Mais cette reference de Moteur vers Voiture doit être une reference faible (d'où le "weak") et pas forte, car sinon tu aurais un cycle de références fortes, Car retenant Motor et Motor retenant Car.
Donc il faut qu'une des deux relations, dans un des 2 sens, soit "weak" et l'autre soit "strong" car si tu mets les 2 à "strong" tu auras un cycle de références fortes qui fera que personne ne sera jamais détruit. (Et si tu mets les 2 à weak personne ne retiendra personne et quand un objet n'est retenu par personne " autrement dit n'a aucune reference forte qui pointe vers lui " alors il est détruit)
Et donc dans ce cas on choisit en general de mettre la référence forte sur l'objet "parent" ou "possesseur" et la reference faible sur l'objet "enfant" ou "possédé".
Ils en parlent aussi ici même si ce guide est un peu plus vieux et a encore des notions de retain/release/dealloc du temps où il n'y avait pas ARC, mais les principes restent les mêmes, il faut juste savoir que quand ils parlent de "retain" (retenir un objet) cela correspond donc à une reference forte (donc une "@property(strong)")
J'ai tout compris. Merci
Quand on connecte un outlet dans une class, la référence est toujours weak car la class hiérarchique (la voiture dans ton example) est la fenêtre (NSWindow). Si la référence est strong, il y'a une référence forte ce qui n'est pas bien.
J'ai une autre question : quand on déclare avec property, est-ce qu'il faut toujours mettre les mots-clé entre parenthèse ?
Là tu parles d'autre chose, similaire pas pas tout à fait identique. On préfère mettre des outlets en référence faible, mais pas pour des raisons de cycle de références.
Un outlet est est un objet membre de la hiérarchie des vues (et donc une référence forte dans la hiérarchie des vues). Si la vue principale est détruite (le NSWindow par exemple) alors tous les objets appartenant à la hiérarchie de vue ne sont plus visibles, et il est logique de les détruire aussi ; d'où les références fortes dans la hiérarchie des vues. C'est le même exemple qu'Ali, avec une fenêtre (à la place de la voiture) et un champ de texte (à la place du moteur).
Pour continuer l'analogie et parler des cycles. Si le champ de texte détient une référence vers sa fenêtre, il est logique que cette référence soit faible pour éviter le cycle de références fortes.
Mais pour l'instant on n'a pas parlé d'outlet. Un outlet est une référence d'un objet de la hiérarchie de vues (pas toujours mais souvent) détenue par un contrôleur. Et un contrôleur est hors de la hiérarchie de vues.
Si l'outlet était en référence forte, l'objet de la hiérarchie de vue ne serait plus détruit lorsque sa vue principale est détruite. On utilise des références faibles pour les outlets pour ne pas empêcher la hiérarchie des vues d'être détruite. C'est très utile sur iOS ; en cas d'alerte mémoire, les hiérarchies de vues qui ne sont pas affichées sont susceptibles d'être détruites. Si les outlets était en référence forte, ce mécanisme ne fonctionnerait pas ce qui provoquerait un crash en cas de mémoire saturée.
Oui
ça fait longtemps que j'ai pas fait d'OSX, mais il me semble que de toute façon si tu n'utilises pas un NSViewController mais directement une NSWindow, de toute façon cette NSWindow est retenue par l'AppDelegate via une @property(strong) quelque part.
Ensuite, chaque NSWindow et NSView retiens ses subviews. Exactement comme je l'expliquais dans mon poste précédent, c'est la vie parenté qui contient ses subviews et donc c'est plutôt elle qui possède ses subviews (c'est elle leur "parent") et à une reference forte vers elles, tandis que les subviews n'ont qu'une reference faible vers leur parent / superview.
Ce qui fait qu'au final la chaà®ne hierarchique va plutôt en descendant, de l'App vers la NSWindow vers ses subviews vers les subviews de ses subviews... etc, chaque parent retenant ses subviews ==> donc au final si tu retiens la vue à la racine de la hiérarchie des vues (en l'occurrence la NSWindow parente de tout ce beau monde) par transitivité tout est retenu déjà .
Du coup ce n'est pas la peine de mettre une autre reference forte vers une des vues quand tu crées un IBOutlet vers elle, ça ferait une reference forte pour rien, avec le problème que si tu fermes la NSWindow et qu'elle est détruite, elle ne va donc plus retenir ses subviews qui vont elles aussi etre détruites (et tant mieux, c'est le but, pour libérer la memoire utilisée par toute la hiérarchie quand la racine n'existe plus)... mais si une de ces subviews avait une autre reference forte vers elle (= ton IBOutlet) en plus de la reference forte de son parent vers elle... du coup elle serait encore retenue et ne serait pas libérée. Tu la retiendrais alors pour rien puisqu'elle ne serait meme plus dans une NSWindow à l'écran.
Un peu comme si tu avais attaché ton chien à un arbre en plus d'avoir une laisse entre le maà®tre et son chien... meme si le maà®tre disparait et relâche le chien, si e dernier est toujours attaché à l'arbre il sera toujours retenu donc existera encore et ne sera pas libéré.
Quand tu veux par exemple "presser le pompon jaune en peluche" pour faire un peti bruit de coin-coin et faire faire risette a ton bébé, tu fais reference (reference faible) à "tiens je vais presser le pompon jaune". Comme tu ferais reference à "je veux mettre du texte dans CE NSTextField". Pour savoir de quel "pompon jaune" ou de quel "textField" tu parles, pour savoir auquel tu fais reference, tu as donc besoin en un IBOutlet pour "pointer dessus", pour te permettre d'y accéder.
Si tu avais une reference forte dessus (pour aller dans le sens de mon allégorie, si tu avais attaché une ficelle de toi vers le pompon jaune pour le retenir), ça veut dire que même si demain tu démontes ton mobile du plafond de la chambre de bébé, donc que tu décroches le crochet du plafond, les bâtons retenus via des ficelles par le crochet vont venir avec, et les bâtons attachés à ces bâtons aussi, et les décorations aussi... mais le pompon jaune lui va rester vivant car il sera toujours accroché à toi par la ficelle que tu as rajoutée. Ce pompon avait déjà une reference forte vers lui " venant du morceau de bois au dessus de lui " et tu en as rajouté une 2ème ce qui fait que meme si tu coupes le fil entre le crochet et les bâtons ou decroches le mobile du plafond (ferme et détruit la NSWindow), le pompon (TextField) restera encore acroché à toi par la 2ème ficelle... ou alors il faut penser aussi à couper cette 2ème ficelle pour être sûr que tout est démonté (détruit) et libéré.
C'est bien plus logique de ne faire qu'une reference faible entre toi et le pompon jaune (donc un IBOutlet "weak" entte ton NSViewController " ou autre " et ton NSTextField), donc de ne pas mettre de ficelle mais juste le montrer du doigt (pour dire "CE pompon jaune" mais pas pour autant y attacher une ficelle / reference forte). Comme ça tu peux toujours pointer dessus / y faire reference (CE pompon que je montre du doigt) mais sans l'attacher à toi et si demain tu démontes le mobile tu es sûr de ne pas risquer d'oublier de détacher le pompon jaune : tu decroches juste le mobile (= tu détruits la NSWindow) et tout le mobile (la hiérarchie de vues) vient avec et rien d'autre be risque de retenir les éléments du mobile donc il sera bien entièrement libre (détruit).
C'est dans cette logique qu'on met donc en général les IBOutlets vers des éléments de ton interface en "weak" (reference faible) : ils sont déjà retenus par leur vue parenté (superview) donc pas la peine de les retenir 2 fois, ça serait comme mettre 2 laisses à un chien.
ça sent le vécu
Merci encore pour ta réponse super clair J'ai compris le système de vue grâce à toi.
Dans les property, quel est la différence entre et retain et assign ?
Assign c'est pour stocker des variables qui ne sont pas des objets (comme une NSView, une instance de Car ou de Motor, ...) mais des valeurs (comme un entier, un float, un BOOL...)