Swift:如何在不等待进程完成的情况下读取子进程中的标准输出
我有一个外部控制台应用程序(在OSX上),它大约每秒发出一次从1到100的整数序列到标准输出 我需要用这些数字来更新进度指标 以下是我目前掌握的代码:Swift:如何在不等待进程完成的情况下读取子进程中的标准输出,swift,cocoa,Swift,Cocoa,我有一个外部控制台应用程序(在OSX上),它大约每秒发出一次从1到100的整数序列到标准输出 我需要用这些数字来更新进度指标 以下是我目前掌握的代码: class MasterViewController: NSViewController { @IBOutlet weak var progressIndicator: NSProgressIndicator! override func viewDidLoad() { super.viewDidLoad() let tas
class MasterViewController: NSViewController {
@IBOutlet weak var progressIndicator: NSProgressIndicator!
override func viewDidLoad() {
super.viewDidLoad()
let task = Process()
task.launchPath = "/bin/sh"
task.arguments = ["-c", "sleep 1; echo 10 ; sleep 1 ; echo 20 ; sleep 1 ; echo 30 ; sleep 1 ; echo 40; sleep 1; echo 50; sleep 1; echo 60; sleep 1"]
let pipe = Pipe()
task.standardOutput = pipe
task.launch()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
if let string = String(data: data, encoding: String.Encoding.utf8) {
print(string)
}
}
代码可以工作,也就是说,它读取命令行实用程序的输出,并相应地修改进度指示器,但它在实用程序退出后进行所有更改(同时使我的UI等待)
我如何设置它,以便它读取后台应用程序的输出并实时更新进度指示器
更新
作为将来的参考,以下是我最终如何让它工作的(现在更新为Swift 3):
现在,一旦子进程完成,进度条将前进到60%,然后跳到100%。您正在主线程上同步读取,因此在函数返回主循环之前,UI不会更新 有(至少)两种可能的方法来解决该问题:
- 在后台线程上读取管道(例如,通过将其分派到后台队列–但不要忘记 将UI更新再次发送到主线程)
- 使用通知从管道异步读取(请参见
例如)李>
class ViewController: NSViewController {
@IBOutlet weak var progressIndicator: NSProgressIndicator!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let task = Process()
task.launchPath = "/bin/sh"
task.arguments = ["-c", "sleep 1; echo 10 ; sleep 1 ; echo 20 ; sleep 1 ; echo 30 ; sleep 1 ; echo 40; sleep 1; echo 50; sleep 1; echo 60; sleep 1"]
let pipe = Pipe()
task.standardOutput = pipe
let outHandle = pipe.fileHandleForReading
outHandle.waitForDataInBackgroundAndNotify()
var progressObserver : NSObjectProtocol!
progressObserver = NotificationCenter.default.addObserver(
forName: NSNotification.Name.NSFileHandleDataAvailable,
object: outHandle, queue: nil)
{
notification -> Void in
let data = outHandle.availableData
if data.count > 0 {
if let str = String(data: data, encoding: String.Encoding.utf8) {
if let newValue = Double(str.trimEverything) {
self.progressIndicator.doubleValue = newValue
}
}
outHandle.waitForDataInBackgroundAndNotify()
} else {
// That means we've reached the end of the input.
NotificationCenter.default.removeObserver(progressObserver)
}
}
var terminationObserver : NSObjectProtocol!
terminationObserver = NotificationCenter.default.addObserver(
forName: Process.didTerminateNotification,
object: task, queue: nil)
{
notification -> Void in
// Process was terminated. Hence, progress should be 100%
self.progressIndicator.doubleValue = 100
NotificationCenter.default.removeObserver(terminationObserver)
}
task.launch()
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
}
// This is just a convenience extension so that I can trim
// off the extra newlines and white spaces before converting
// the input to a Double.
fileprivate extension String {
var trimEverything: String {
return self.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
}
}