首页 > 解决方案 > 如何在swift中进行函数回调

问题描述

在开始之前,我知道有很多与此类似的问题。但是,我认为 swift 的较新版本可能使它们过时了,因为它们似乎对我不起作用。

这是我的问题:我需要将一个函数作为参数传递给另一个函数(回调)。但是,我无法弄清楚如何在 Swift 5.3.3 中执行此操作(我很确定这是我的版本)

我想制作一个 Mac 应用程序来控制我的许多不和谐机器人中的哪些正在运行。我的不和谐机器人是执行永无止境的 python 文件。因此,如果我使用 swift 的 shell 执行它们,那行代码将永远不会结束。因此,我想异步执行每个机器人,这样应用程序就不会冻结。我目前正在通过制作DispatchWorkItem, 然后DispatchQueue.main.async(execute:DispatchWorkItem)用于执行机器人来做到这一点。但是,制作 aDispatchWorkItem需要回调函数来执行(我忽略了其他看似可选的参数)。

这就像我正在尝试做的事情:

let workItem=DispatchWorkItem(**INSERT CALLBACK(to exec function defined below) HERE**)
DispatchQueue.main.async(workItem)

我还没有让它成功运行。我几乎没有 swift 方面的经验,所以我对自己做错了什么没有一个超级坚定的想法。该代码中应该用什么\*\*INSERT CALLBACK(to exec function below) HERE\*\*替换?如果您需要更多信息,请告诉我。

谢谢!

以下是我的代码的所有相关部分:

 func shell(_ command: String) -> String {
        let task = Process()
        let pipe = Pipe()

        task.standardOutput = pipe
        task.standardError = pipe
        task.arguments = ["-c", command]
        task.launchPath = "/bin/zsh"
        task.launch()

        let data = pipe.fileHandleForReading.readDataToEndOfFile()
        let output = String(data: data, encoding: .utf8)!

        return output
    }
//for the record I did not write that function(got it from this site) and sort of vaguely understand it but not really

 func exec(){
                let result = shell("/usr/local/bin/python3.9 path/to/python/file")
                print(result)
            }

//this has been proven to work, it just freezes that app because the above function will never end. This is why I want it to be asynchronous

标签: swiftasynchronouscallback

解决方案


与其直接回答,我首先想向您简要介绍一下回调函数。

简而言之:在大多数现代编程语言中,函数可以作为另一个函数中的参数/参数:

function doHomework(subject, finishedHomework()) {
    print('Started my homework!');
}

在上面的简化案例中,doHomework 是开始执行之前的回调函数(finishedHomework)。如果我们这样称呼它:

doHomework('IT', () -> { print('Finished my homework!); });

或者

finishedHomework() { print('Finished my homework!') }
doHomework('IT', finishedHomework);

我们将看到一个输出,说我们已经开始在第一行打印出我们的作业,并且我们已经在下面的行中完成了我们的作业。我把它放在一行上,这样你就可以看到括号是如何等同的。我们这样做是因为在我们的第一个函数执行延迟的情况下(即 API 调用、复杂计算),它仍然会先执行,然后按该顺序执行第二个函数。将回调的执行视为依赖于初始函数;如果我们开始做作业,但发生其他事情阻止我们,我们就不会完成它。

在 Swift 中,我相信回调也被称为闭包函数,通常与完成处理程序配对,为了简单起见,我不会过多介绍。这是另一个例子,但这次是在 Swift 中传递一个值:

let closureFunction = {
    (x: Int) -> String in
    return "The number in our closure function is: \(x)."
  }
  func funky(callback: (x: Int) -> String) {
    callback(13)
  }
  funky(closureFunction) /** The number in our closure function is: 13. */

请记住,在 Swift 5 中,我们仍然可以定义如下函数:

(String)->() // takes a String returns void
()->(String) // takes void returns a String

闭包语法通常采用以下形式:{ (parameters) -> return type in statements }

免责声明:我没有在下面测试过这段代码,但希望它是朝着正确方向迈出的好一步。

let shell = { 
    (_ command: String) -> String in
        let task = Process()
        let pipe = Pipe()

        task.standardOutput = pipe
        task.standardError = pipe
        task.arguments = ["-c", command]
        task.launchPath = "/bin/zsh"
        task.launch()

        let data = pipe.fileHandleForReading.readDataToEndOfFile()
        let output = String(data: data, encoding: .utf8)!

        return output
    }

    func exec(callback: (command: String) -> String) {
        let result = callback("/usr/local/bin/python3.9 path/to/python/file")
        print(result)
    }

    exec(shell)

我对使用 DispatchWorkItem 不熟悉,所以可以考虑学习关于 DispatchQueue 基础知识甚至 DispatchSemaphore 的快速教程。无论哪种方式,我认为这应该足以让您获得足够的信息来进行机器人管理,而无需让某人为您完成工作。从这里将回调添加为 DispatchQueueItem 应该很容易。

让社区知道您是否仍然面临问题。

Swift 5 中的调度队列

DispatchGroups 和信号量


推荐阅读