Probleme NSThread et accesseurs
vincentf213
Membre
Bonjour à tous,
Je poste ce message parce que je rencontre un problème sur lequel je sèche complètement Voila le contexte : je suis en train de faire mes armes sur le développement Cocoa en créant un mini lecteur MP3 très basique. J'ai donc un fenêtre qui s'ouvre avec un bouton qui me permet de sélectionner un fichier audio et un bouton qui me permet de lire ce fichier. Tout ceci fonctionne plutôt bien.
Pendant la lecture, je souhaite mettre à jour un NSSlider ainsi qu'un champ texte sur ma fenêtre indiquant le temps. Comme cette mise à jour doit se faire en continue j'ai mis en place, non sans mal, un Thread qui exécute une méthode pour faire avancer le slider et changer le valeur du texte.
Maintenant, et c'est la que les ennuis commencent, je voudrais que lorsque je change la valeur de mon slider en cliquant directement dessus mon instance de NSSound utilise cette valeur pour se mettre à jour et joue ma musique à partir du temps t (une sorte d'avance rapide en somme). J'en arrive donc a mon problème : mon thread s'exécutant en tâche de fond met à jour la valeur du slider avec le temps courant de mon instance NSSound avant ma méthode "avance rapide". Ainsi quand elle s'exécute la valeur du slider n'est pas celle que j'ai indiquée en cliquant dessus mais celle mis à jour par le thread, celle du temps avant le clique donc (je l'admets c'est un peu confus).
Ce que je cherche à faire c'est qu'au moment ou je clique sur mon slider, mon thread s'arrête, laisse ma méthode "avance rapide" s'exécuter et reprenne son travail après J'ai bien essayé de le stopper au début de la méthode avance rapide mais la mise a jour se fait avant, j'ai aussi réglé la priorité du thread sans grand résultat.
Voila je fais donc appel à vos lumières. J'en profite aussi pour vous demander si le thread est une bonne idée ou s'il existe d'autre moyen d'exécuter des méthode en tâche de fond sans bloquer l'application
Merci pour aide,
Vincent
Je poste ce message parce que je rencontre un problème sur lequel je sèche complètement Voila le contexte : je suis en train de faire mes armes sur le développement Cocoa en créant un mini lecteur MP3 très basique. J'ai donc un fenêtre qui s'ouvre avec un bouton qui me permet de sélectionner un fichier audio et un bouton qui me permet de lire ce fichier. Tout ceci fonctionne plutôt bien.
Pendant la lecture, je souhaite mettre à jour un NSSlider ainsi qu'un champ texte sur ma fenêtre indiquant le temps. Comme cette mise à jour doit se faire en continue j'ai mis en place, non sans mal, un Thread qui exécute une méthode pour faire avancer le slider et changer le valeur du texte.
Maintenant, et c'est la que les ennuis commencent, je voudrais que lorsque je change la valeur de mon slider en cliquant directement dessus mon instance de NSSound utilise cette valeur pour se mettre à jour et joue ma musique à partir du temps t (une sorte d'avance rapide en somme). J'en arrive donc a mon problème : mon thread s'exécutant en tâche de fond met à jour la valeur du slider avec le temps courant de mon instance NSSound avant ma méthode "avance rapide". Ainsi quand elle s'exécute la valeur du slider n'est pas celle que j'ai indiquée en cliquant dessus mais celle mis à jour par le thread, celle du temps avant le clique donc (je l'admets c'est un peu confus).
Ce que je cherche à faire c'est qu'au moment ou je clique sur mon slider, mon thread s'arrête, laisse ma méthode "avance rapide" s'exécuter et reprenne son travail après J'ai bien essayé de le stopper au début de la méthode avance rapide mais la mise a jour se fait avant, j'ai aussi réglé la priorité du thread sans grand résultat.
Voila je fais donc appel à vos lumières. J'en profite aussi pour vous demander si le thread est une bonne idée ou s'il existe d'autre moyen d'exécuter des méthode en tâche de fond sans bloquer l'application
Merci pour aide,
Vincent
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
En pratique, c'est déconseillé d'utiliser les NSThread. Il existe des techniques bien plus avancées pour faire de l'exécution parallèle. Manipuler les threads directement n'est pas simple car il y a plein de choses à prendre en considérations, protection de la mémoire, accès concurrents au resources, etc. Il y a d'autres outils bien plus adaptés pour ton cas qui t'éviterons bien des déboires.
Déjà , pour ton cas, un NSTimer suffit amplement. Il va te permettre de demander d'exécuter un bout de code toutes les N secondes, pour mettre à jour ton interface. Dans ton cas c'est exactement ce qu'il te faut. Et c'est 1000x plus simple qu'un NSThread (ça n'a même rien à voir, bien plus simple à manipuler). Tu peux demander au NSTimer de se lancer, l'arrêter quand tu as besoin, le recréer quand tu veux redémarrer ton timer, etc...
Et ça ne crée pas de thread dédié qui prendrait des ressources inutiles (swapping de contexte mémoire, etc, beaucoup d'overhead pour rien si tu utilisais un NSThread), car ça s'installe tout seul automatiquement sur la RunLoop.
Après, pour ta culture, si un jour tu avais vraiment à exécuter du code en tâche de fond, façon thread, de toute façon ne pars pas direct sur NSThread. Car ce n'est pas la solution la plus simple et l'API la plus adaptée la plupart du temps. Il y a d'autres possibilités, comme bien souvent déjà des APIs asynchrones existant pour ce que tu veux faire, ou des NSOperation, ou GCD... qui sont toutes plus faciles à utiliser qu'un NSThread et évitent bien des problèmes que tu peux avoir avec ceux-ci vu les considérations que tu dois prendre en compte avec NSThread.
Apple a une doc dédiée sur le sujet, et entre autres un paragraphe dédié "Migrating away from Threads" qui explique qu'il y a bien d'autres façons de faire maintenant avec les API modernes pour utiliser autre chose que les threads, du moins de façon directe avec NSThread
Merci pour l'aide et pour le lien je vais lire ça un peu plus en détail. Je ne peux pas tester la solution maintenant mais je reviendrai vous tenir au courant.
Pas besoin d'arrêter le timer avant et de le relancer après. Pas de risque de "Race Condition" comme tu peux avoir sur les NSThreads, car un NSTimer ne génère pas un thread séparé, il est exécuté par la RunLoop (par défaut, comme l'a mentionné Céroce, sur la RunLoop du thread principal, ou plus exactement du thread sur lequel tu as installé ton timer mais en général tu le fais dans le Main Thread de toute façon)
Et c'est tout ! Pas de threads, pas de gros code à gérer ou de start/stop ou quoi, juste un NSTimer qui déclenche une méthode régulièrement, que tu crées au début et que tu arrêtes à la fin.
En gros, sous OS X, l'équivalent de viewDidLoad est -awakeFromNib. Je dis, en gros, parce que c'est équivalent fonctionnellement, mais pas dans le principe.
(La méthode awakeFromNib d'un objet est appelée lorsqu'un objet est instancié depuis un fichier .nib. Mais elle est également appelée sur le File's Owner, c'est à dire l'objet à l'origine du chargement du fichier nib. Le File's Owner sera donc le NSWindowController ou le NSViewController).
Mais bon, le principe de mon exemple c'était juste pour te montrer :
Voilà j'espère que c'est plus clair /wink.png' class='bbc_emoticon' alt=';)' />
Donc en gros tu as juste à utiliser la méthode "scheduleTimer..." pour créer ton timer (et définir sa période + la méthode à appeler à chaque itération) et la méthode "invalidate" (+ remise à nil) pour l'arrêter.
Un grand merci pour toutes ces précisions.
Sache tout de même que les applis sous OS X prennent leurs aises...