2010-07-21 21 views
1

J'ai couru dans un moment en essayant de lancer un NSTask depuis l'intérieur d'un NSOperation.Pourquoi un NSTask auto-libéré bloque-t-il indéfiniment la boucle de lancement sur un thread NSOperation?

Voici un very simple app que j'ai mis ensemble pour mettre en évidence le problème. Lorsque vous cliquez sur le bouton, un NSOperation est mis en file d'attente. Il configure un NSRunLoop et appelle une méthode qui appelle un NSTask. La tâche est vraiment simple - il lance simplement /bin/sleep pendant deux secondes (assez pour voir facilement le spinner quand les choses fonctionnent correctement). L'application fonctionne comme annoncé, cependant si vous changez la ligne 23 de TaskPerformer.m à autorelease, (désolé, je suis un nouvel afficheur donc je ne peux pas lier directement) ou le commenter complètement (fuyant ainsi le Objet NSTask), le thread NSOperation ne sera jamais quitter. Son runloop principal semble bloquer quelque chose.

Maintenant, le problème est double. Tout d'abord, je ne comprends pas pourquoi mon thread bloque, mais de plus, si j'active le garbage collection pour cette application, le même comportement se manifeste. Parce qu'il n'y a aucun moyen pour moi de libérer manuellement le NSTask, le thread se bloque quoi qu'il arrive.

Si quelqu'un pouvait me dire ce qui se passait, je serais éternellement reconnaissant!

Répondre

1

J'ai compris ce qui se passait ici. Il s'avère que mon appel à [runLoop run] mettait en place la boucle et l'exécutait indéfiniment. Il ne tombait jamais dans la boucle while (!done). Il s'avère qu'après avoir appelé run, un NSRunLoop s'exécutera jusqu'à ce qu'il n'ait plus d'entrées. Appeler release sur mon NSTask conduit exactement à ce scénario, donc (tout à fait par accident) mon runloop est sorti.

La solution était d'enlever [runLoop run] et de simplement compter sur ma propre boucle while. J'espère que cela aide quelqu'un d'autre!

4

Je vois quelques problèmes différents dans l'exemple de projet que vous avez publié. En TaskPerformer.m vous avez:

[task waitUntilExit]; 
[task launch]; 

L'appel waitUntilExit est destiné à être appelé après le lancement la tâche, et sera simplement bloquer et ne rien faire jusqu'à ce que la tâche soit terminée. Si vous vous souciez seulement d'attendre que la tâche soit finie, et non d'en tirer quelque chose, alors vous devriez pouvoir appeler launch suivi de waitUntilExit, et ne pas avoir à vous embêter avec la boucle d'exécution.

Si vous voulez obtenir la sortie de la tâche cependant, alors vous voulez obtenir son standardOutput, qui par défaut devrait vous renvoyer une instance de NSFileHandle. Vous pouvez ensuite appeler readDataOfLength: ou readDataToEndOfFile, qui bloquent et renvoient des données du processus lorsque les données sont disponibles. Puisque tout ceci va être fait dans un thread d'arrière-plan de toute façon, c'est OK que ces méthodes bloquent, mais vous ne voudriez pas faire la même chose sur le thread principal, car cela verrouillerait l'interface pour l'utilisateur jusqu'à ce que la tâche soit terminée. Si vous l'avez fait sur le fil principal, vous voudrez utiliser le NSFileHandlereadInBackgroundAndNotify et vos amis. Pour un fil de fond cependant, en utilisant NSRunLoop ne devrait pas vraiment être nécessaire.

+0

Merci d'avoir signalé cela, Brian. Je l'avais déjà vu, je n'avais pas poussé le code mis à jour vers Github.Fixé maintenant Malheureusement, un 'NSRunLoop' est nécessaire dans mon application actuelle, car en plus de lancer un tas de' NSTask', j'utilise aussi des 'NSURLConnection' asynchrones qui nécessitent un' NSRunLoop' pour leurs rappels. Ce qui est également intéressant, c'est que la suppression de l'appel de 'waitUntilExit' fait en sorte que le thread se termine la première fois, mais pas les fois suivantes. Quelque chose me semble vraiment bizarre. – texel