指定完成处理程序的更短方法

Posted

技术标签:

【中文标题】指定完成处理程序的更短方法【英文标题】:Shorter ways to specify completion handler 【发布时间】:2016-02-20 13:08:29 【问题描述】:

我已经遇到过几次这个问题了,所以我想联系一下。

我有许多网络接口负责进行异步网络调用,每个接口中大约有 5/6 个函数都使用具有相同定义的完成处理程序:

(success: Bool, resources: [String: AnyObject] -> Void)

我正在寻找一种替代方法来将它添加到每个函数的末尾,因为它强制每个函数声明到 2/3 行。例如

func performSomeNetworkCall(withThisParam parm1: AnyObject, param2: AnyObject, completion: (success: Bool, resources: [String: AnyObject] -> Void)) 


关于如何解决长声明问题和重写每个函数的完成定义,我有几个想法。

想一想

使用typealias 来定义闭包,如下所示:

typealias CompletionHandler = (success: Bool, resources: [String: AnyObject] -> Void)?

理论上,这解决了这两个问题,因为它可以快速编写并解决代码长度问题。但是,当从外部源调用函数时,typealias 不会像常规闭包那样自动完成,这意味着您必须每次都将其写出来,从而导致错误。

思想二

使用代码 sn-p(当 Xcode 真正记住你设置它们时)。这在理论上也是有效的,只要所有其他开发代码的开发人员也有相同的代码 sn-p 并且它知道它而不是编写整个声明。

三思

我想过将完成处理程序定义为一个函数并将该函数作为参数传递,如下所示:

func completionHandler() -> (success: Bool, resources: [String: AnyObject] -> Void) 
        return completionHandler()
    

但无法实现我想要的。

编辑

我希望通过第三个想法实现的目标

func completionHandler() -> ((success: Bool, resources: [String: AnyObject]) -> Void) 
    return completionHandler()


func networkCall(completion: (success: Bool, resources: [String: AnyObject]) -> Void) 
    let request = NSURLRequest(URL: NSURL(string: "http://www.google.co.uk")!)

    let session = NSURLSession.sharedSession()

    session.dataTaskWithRequest(request)  (data, response, error) -> Void in
        let handler = completionHandler()
        handler(success: true, resources: [String: AnyObject]())
    


func foo() 
    let handler = completionHandler()
    networkCall(handler)

    print(handler)
    // hope to use handler.success || handler.resources here

虽然目前只是卡在了 completionHandler 方法调用上——(指出显而易见的......)

【问题讨论】:

我的第一直觉会被认为是三个。不知道为什么这不起作用?你没有达到什么目标?是错误还是没有按您预期的方式工作? @AtheistP3ace 我已经编辑了这个问题,以扩展我希望通过思想三实现的目标。目前它只是在completionHandler() 调用上循环。在尝试使用 foo() 中的处理程序时,我也没有从 Xcode 得到任何建议。例如不建议handler.success 【参考方案1】:

typealias 是解决此问题的典型解决方案。即使在原始定义的文件之外,它也应该自动完成。我认为问题在于您对typealias 的定义。如果你这样定义(注意括号,没有可选的):

typealias CompletionHandler = (success: Bool, resources: [String: AnyObject]) -> Void

func performSomeNetworkCall(withThisParam parm1: AnyObject, param2: AnyObject, completion: CompletionHandler?) 
    // Implementation

然后自动完成它的工作方式如下:

按回车键后:

【讨论】:

【参考方案2】:

我正在寻找替代方案

恕我直言,编写更简洁代码的更好选择是使用“Promises”或“Futures”。这些还不是 Swift 标准库的一部分,但其他语言确实有一种或另一种实现 - 并且已经有 Swift 的第三方库。

例如,使用“类 Scala”Futures,您的代码可能如下所示:

func performSomeNetworkCall(
    withThisParam parm1: AnyObject, 
    param2: AnyObject) 
    -> Future<[String: AnyObject]>

因此,异步函数返回一个Future,而不是完成处理程序。 future 是一个泛型类,其类型参数等于计算值。如果底层异步函数失败,未来也可能表示错误。

您可以通过 combinator 函数注册一个延续来获得最终结果:

let future = performSomeNetworkCall(param1, param2: param2)
future.map  computedValue in
    print("Result: \(computedValue)")

.onFailure  error in
    print("Error: \(error)")

这里,map 是一个 combinator 函数,我们可以使用它注册一个 continuation。组合器根据其延续的返回值返回一个新的未来,因此我们可以组合执行更复杂任务的未来。

当future 成功完成时,将调用延续函数。它通过底层异步函数的 value 作为其参数。延续返回一个Void

另一个组合器是flatMap。与map 不同,它的延续只是返回另一个未来:

login().flatMap  token in
    fetchUser(id: userId).map  user in 
        print("User: \(user)")
    
.onFailure  error in
    print("Error: \(error)")

函数login 是一个异步函数。成功完成后,它继续调用异步函数fetchUser。当fetchUser成功完成后,打印获取到的用户对象。

如果发生任何故障,错误将传播到已在onFailure注册的“故障处理程序”。

【讨论】:

这是一个非常有趣的概念,当然是“我正在寻找替代方案”,我没想到会这样,所以谢谢!你能推荐一个第三方库吗?我在 Google 搜索中看到了几个不同的选项。 @Ollie 对于类似 Skala 的期货,有 Thomas Visser 的 BrightFutures - 非常完整和高质量。然后是 Tuomas Kareinen 的 MiniFuture,简约但正确且高质量的代码。我自己的尝试:FutureLib。这可与 BrightFutures 相媲美,但具有更灵活的取消和更灵活的关于抛出延续的 API 和更强大的执行上下文。还有其他库——基于 Objective-C。

以上是关于指定完成处理程序的更短方法的主要内容,如果未能解决你的问题,请参考以下文章

使用 Maybe 遍历嵌套记录的更短方法

Powershell:使用许多文件名执行命令的更短方法?

检查 jquery 的多个输入的值是不是为空的更短方法

从数组中按索引删除项目的更短方法[重复]

在JS中编写三元运算符的更短方法[重复]

CASE 表达式 WHEN 的更短替代方案?