LoginSignup
8
8

More than 5 years have passed since last update.

NSTaskを非同期で実行する

Last updated at Posted at 2016-09-07

UNIXコマンドを非同期で実行する

SwiftでMacアプリを作るときに。

参考:UNIXコマンドを実行する(非同期)
参考:【OSX】【swift】コマンドの実行

非同期で、対話式に出力を取りたい時、少し罠があるのと、面倒が多いので、罠を乗り越えたクラスを作りました。

リポジトリ:https://github.com/ha1fha1f/CommandTask

重要なのはCommandTask.swiftだけです。

使い方は、

let task = CommandTask(cmd: "/usr/bin/curl", arguments: ["https://qiita.com"])
            .addObserver { string in
                print("observe", string)}
            .addCompletionHandler {
                print("complete")}
            .launch()

とするだけです。(参照:ViewController.swift

自分のPCで実行する場合、コマンドのパスは、ターミナルで which コマンド名 とすれば調べられます。

対話式に入力したいときは、

task.writeln("今日はいい天気ですね")

を実行すればよいです。よく考えれば当たり前なのですが、writeのあとに "\n"を入れないと入力が完了しないのに、入れ忘れて結構時間無駄にしましたので、自動で入力するメソッドを用意しました。

その他罠としては、terminationHandlerが呼ばれたあとも、NSFileHandleDataAvailableNotificationのオブザーバーが呼ばれる点です。これには完了処理を呼ばれたあとで判断することによって対処しました。

ネットのコードを見ていると、 NSTaskDidTerminateNotification を用いているものもありますが、標準で用意されている terminationHandler を用いたほうが良いと思います。タイミングは同じです。

実行ファイルをプロジェクトの中に入れる

実行ファイルのバイナリは、もちろんアプリの中に入れることもできます。archiveしたあとも、.app内部に入ります。

その場合は、プロジェクト内の適当なところにファイルを配置しておいて、

NSBundle.mainBundle().pathForResource("ファイル名", ofType: "")

とすれば取得できます。これをCommandTaskのイニシャライザの引数にセットすればOKです。

不安点

メモリ管理、どうなってるのかわからなくて不安なので、詳しい方はコメントいただけると嬉しいです。
現状のコードだと、commandTaskのインスタンスを、viewcontrollerのプロパティなどとして保存しておかない)と、参照が0になって、commandTaskのプロパティとして保存しているobserverとかが消失して、呼ばれなくなるので、その辺修正の必要を感じています

8
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
8