如何将一系列相关的 NSTask 操作包装成离散函数
How do I wrap up a series of related NSTask operations into discrete functions
我正在编写一个 Swift 命令行工具,它使用 NSTask
与 git
交互。在最简单的场景中,我想要 运行 三个命令:init
、add .
和 commit -m Initial Commit
。我打算为每个命令使用一个单独的 NSTask
,并希望将每个命令放在它自己的函数中 - 如果任务成功,则为 returning true
,否则为 false
't。此设置将使我的 main
函数看起来像这样:
func main() {
if runInit() {
if runStage() {
if runCommit() {
NSLog("success!")
}
}
}
}
要完成此操作,三个函数中的每一个都必须在 returning 之前执行以下操作 (i) 启动任务 (ii) 等待它完成,(iii) 获取 [=21= 中的任何内容],以及 (iv) 设置 return 值(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 的文档说:
This method first checks to see if the receiver is still running using isRunning. Then it polls the current run loop using NSDefaultRunLoopMode until the task completes.
当谈到 运行 循环机制时,我有点不知所云,想知道这在这种情况下意味着什么 - 我可以安全地假设我的通知块将 始终 在封闭函数 returns?
之前执行
waitUntilExit
returns 当收到 SIGCHILD
信号时
表明子进程已经终止。通知书
当 EOF
从管道读取到子进程时执行块。
未指定这些事件中的哪一个先发生。
因此您必须等待两者。有几种可能的解决方案,
这是一个使用 "signalling semaphore",你也可以使用
"dispatch group".
您的代码中的另一个错误是永远不会删除观察者。
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
}
我正在编写一个 Swift 命令行工具,它使用 NSTask
与 git
交互。在最简单的场景中,我想要 运行 三个命令:init
、add .
和 commit -m Initial Commit
。我打算为每个命令使用一个单独的 NSTask
,并希望将每个命令放在它自己的函数中 - 如果任务成功,则为 returning true
,否则为 false
't。此设置将使我的 main
函数看起来像这样:
func main() {
if runInit() {
if runStage() {
if runCommit() {
NSLog("success!")
}
}
}
}
要完成此操作,三个函数中的每一个都必须在 returning 之前执行以下操作 (i) 启动任务 (ii) 等待它完成,(iii) 获取 [=21= 中的任何内容],以及 (iv) 设置 return 值(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 的文档说:
This method first checks to see if the receiver is still running using isRunning. Then it polls the current run loop using NSDefaultRunLoopMode until the task completes.
当谈到 运行 循环机制时,我有点不知所云,想知道这在这种情况下意味着什么 - 我可以安全地假设我的通知块将 始终 在封闭函数 returns?
之前执行waitUntilExit
returns 当收到 SIGCHILD
信号时
表明子进程已经终止。通知书
当 EOF
从管道读取到子进程时执行块。
未指定这些事件中的哪一个先发生。
因此您必须等待两者。有几种可能的解决方案, 这是一个使用 "signalling semaphore",你也可以使用 "dispatch group".
您的代码中的另一个错误是永远不会删除观察者。
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
}