我正在编写一个 Swift 命令行工具,该工具使用NSTask
与git
进行交互。在最简单的场景中,我想运行三个命令:init
,add .
和commit -m Initial Commit
。我打算为每个命令使用单独的NSTask
,并希望将每个命令放在自己的函数中 - 如果任务成功则返回true
,如果没有,则返回false
。这个设置允许我的main
函数看起来像这样:
func main() {
if runInit() {
if runStage() {
if runCommit() {
NSLog("success!")
}
}
}
}
为了实现这一点,在返回(i)启动任务(ii)等待它完成,(iii)获取stdout
中的任何内容,以及(iv)设置之前,三个函数中的每一个必须执行以下操作。返回值(true
或false
)。这是我在提交阶段所获得的:
func runCommit() -> Bool {
var retval = false
var commitTask = NSTask()
commitTask.standardOutput = NSPipe()
commitTask.launchPath = gitPath
commitTask.arguments = ["commit", "-m", "Initial Commit"]
commitTask.currentDirectoryPath = demoProjectURL.path!
commitTask.standardOutput.fileHandleForReading.readToEndOfFileInBackgroundAndNotify()
nc.addObserverForName(NSFileHandleReadToEndOfFileCompletionNotification,
object: commitTask.standardOutput.fileHandleForReading,
queue: nil) { (note) -> Void in
// get the output, log it, then...
if commitTask.terminationStatus == EXIT_SUCCESS {
retval = true
}
}
commitTask.launch()
commitTask.waitUntilExit()
return retval
}
我的问题主要是关于waitUntilExit
如何工作,特别是与我注册的通知一起使我能够获得输出。 Apple的文档说:
此方法首先检查接收器是否仍在使用isRunning运行。然后,它使用NSDefaultRunLoopMode轮询当前的运行循环,直到任务完成。
当谈到运行循环机制时,我有点超出了我的深度,并且想知道在这种情况下这意味着什么 - 我可以安全地假设我的通知块将始终被执行在封闭函数返回之前?
答案 0 :(得分:3)
waitUntilExit
信号后, SIGCHILD
返回
表明子进程已终止。通知
从管道向子进程读取EOF
时执行块。
not 指定了哪些事件首先发生。
因此你必须等待两者。有几种可能的解决方案, 这里有一个使用“信号信号量”,你也可以使用 一个“派遣小组”。
代码中的另一个错误是永远不会删除观察者。
func runCommit() -> Bool {
let commitTask = NSTask()
commitTask.standardOutput = NSPipe()
commitTask.launchPath = gitPath
commitTask.arguments = ["commit", "-m", "Initial Commit"]
commitTask.currentDirectoryPath = demoProjectURL.path!
commitTask.standardOutput!.fileHandleForReading.readToEndOfFileInBackgroundAndNotify()
let sema = dispatch_semaphore_create(0)
var obs : NSObjectProtocol!
obs = nc.addObserverForName(NSFileHandleReadToEndOfFileCompletionNotification,
object: commitTask.standardOutput!.fileHandleForReading, queue: nil) {
(note) -> Void in
// Get data and log it.
if let data = note.userInfo?[NSFileHandleNotificationDataItem] as? NSData,
let string = String(data: data, encoding: NSUTF8StringEncoding) {
print(string)
}
// Signal semaphore.
dispatch_semaphore_signal(sema)
nc.removeObserver(obs)
}
commitTask.launch()
// Wait for process to terminate.
commitTask.waitUntilExit()
// Wait for semaphore to be signalled.
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER)
let retval = commitTask.terminationStatus == EXIT_SUCCESS
return retval
}