如何在 Swift / Xcode 12.3 中重用代码块进行闭包

Posted

技术标签:

【中文标题】如何在 Swift / Xcode 12.3 中重用代码块进行闭包【英文标题】:How do I re-use a block of code for a closure in Swift / Xcode 12.3 【发布时间】:2021-01-20 16:00:51 【问题描述】:

在我的 ios 应用程序中,我正在运行一个 HKStatisticsCollectionQuery,它有一个

query.initialResultsHandler =  query, results, error in [...] 

还有一个

query.statisticsUpdateHandler =  query, hkStats, results, error in [...] 

两个闭包完全相同。

有没有办法只编写一次这些闭包的代码并引用它两次?通常我会重构/提取一个函数并调用它,但在这里我有一个

DispatchQueue.main.async  ... 

在处理程序内部,所以我不确定它是否会工作...正如您可能从我的问题中推断的那样,我不确定我是否正在关闭和 dispatchQueue 好吗...

【问题讨论】:

【参考方案1】:

闭包和函数是可以互换的。任何需要传递闭包的地方,都可以传递函数。

因此,您可以为这两种情况编写一个函数并传递该函数名而不是闭包。

也就是说,您的两个示例显示了不同的参数,这与您“两个闭包完全相同”的说法相矛盾。如果他们取不同的参数,怎么可能是一样的呢?

编辑:

考虑以下函数:

func foo(value: String, completion: (String)->Void) 
    completion(value)

如您所料,我们可以使用闭包调用 foo:

foo(value: "firstCall", completion:  value in
    print("in completion handler, value = '\(value)'")
)

那会输出

在完成处理程序中,值 = 'firstCall'

我们还可以定义一个或多个签名匹配完成处理程序的函数:

func completionFunction(value: String) 
    print("In \(#function), value = '\(value)'")


func completionFunctionTwo(value: String) 
    print("In \(#function), value = '\(value)'")

然后这样调用:

foo(value: "secondCall", completion: completionFunction)
foo(value: "thirdCall", completion: completionFunctionTwo)

请注意我们只是将函数名称作为完成处理程序传递。这相当于传递一个内联闭包。

那些调用会输出:

在completionFunction(value:)中,value = 'secondCall'

在 completionFunctionTwo(value:), value = 'thirdCall'

最后,您可以创建一个包含闭包的变量,并将其作为您的完成处理程序传递:

let aCompletionClosure: (String) -> Void =  value in
    print("In completion closure, value = '\(value)'")


foo(value: "forthCall", completion: aCompletionClosure)

输出:

在完成闭包中,value = 'forthCall'

【讨论】:

我没有在更新处理程序中使用“额外”的 hkStats。它就在那里,因为在苹果的文档中,它是传递给闭包的第二个属性。我仍然不知道我是否可以忽略它,但这是一个不同的话题......也许稍后。 - - 反正。非常感谢您的长回答。我得消化一下,可能需要一段时间......【参考方案2】:

由于你的闭包有不同的参数,你需要创建一个函数,它可以在两个闭包中调用:

func handle(query: ...?, results: ...?, error: Error?) 
    //...

由于第一个处理程序的参数与您的函数完全相同,您可以简单地将其分配给处理程序:

query.initialResultsHandler = handle

或者你可以从你的闭包中调用它:

query.initialResultsHandler =  query, results, error in 
    self.handle(query: query, results: results, error: error)

在第二个处理程序中,参数不同,所以你可以调用它:

query.statisticsUpdateHandler =  query, hkStats, results, error in 
    self.handle(query: query, results: results, error: error)

或者,您可以定义第二个函数,它匹配第二个闭包签名,并从中调用第一个函数:

func handleUpdate(query: ...?, hkStats: ...?, results: ...?, error: Error?) 
    handle(query: query, results; results, error: error)

在这种情况下,您还可以将该函数分配给第二个处理程序:

query.statisticsUpdateHandler = handleUpdate

【讨论】:

以上是关于如何在 Swift / Xcode 12.3 中重用代码块进行闭包的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Xcode 5 中重命名项目?

如何在 Xcode 5 中重命名模拟器?

如何在 SWIFT 中重绘我的视图?

超实用~使用 Xcode 编译 Swift 源码

可可豆荚的 Xcode 12.3 问题

Xcode 12.3 Mac Catalyst 代码签名失败(代码对象根本没有签名)