【问题标题】:Swift closures - force a closure to always completeSwift 闭包 - 强制闭包始终完成
【发布时间】:2017-08-29 17:07:17
【问题描述】:

是否可以强制关闭完成?与具有返回值的函数必须始终返回一样,如果有办法强制闭包包含始终完成所需的语法,那将是王牌。

例如,此代码不会编译,因为函数并不总是返回值:

func isTheEarthFlat(withUserIQ userIQ: Int) -> Bool {
    if userIQ > 10 {
        return false
    }
}

以完全相同的方式,我想定义一个带有闭包的函数,如果闭包永远不会返回,它也不会编译。例如,下面的代码可能永远不会返回completionHandler:

func isTheEarthFlat(withUserIQ userIQ: Int, completionHandler: (Bool) -> Void) {
    if userIQ > 10 {
        completionHandler(false)
    }
}

上面的代码可以编译,但我想知道是否有一个关键字强制闭包在所有情况下都发送一个完成处理程序。可能和上面函数中的Void有关?

【问题讨论】:

  • 不是语言级别,尽管我对如何实现它有一个有趣的想法。让我尝试一些东西
  • 你不能在方法的最后调用完成处理程序吗?
  • @Sweeper 我们的大多数完成处理程序都用于服务器访问,因此它们将返回 (1) 发生的任何错误和 (2) 来自服务器的数据。由于服务器错误与我们希望向用户显示的错误不同,因此我们通常有很多功能可以决定错误是什么,同样我们可能希望以特定方式操作从服务器返回的数据。我所要求的只是编译器确保在代码的所有分支中调用完成处理程序 - 与在普通函数中必须返回返回值的方式完全相同。
  • "我只要求编译器确保在代码的所有分支中调用完成处理程序——与在普通函数中必须返回返回值的方式完全相同。 "您无法让编译器帮助您,但从程序员的角度来看,defer 语句可以满足您的需求。
  • SE-0073 会给你这个,但它被拒绝在命名理由(它被提议作为@noescape 的扩展,现在已弃用)以及需要大量工作的底层实现.希望它是一种能够进入该语言未来版本的功能。

标签: swift closures


【解决方案1】:

不,如果您忘记(或不需要)在所有可能的条件下(例如 return 语句)调用完成处理程序,则没有任何语言结构会导致编译器错误。

这是一个有趣的想法,可能会对语言进行有用的增强。可能作为参数声明中某处的required 关键字。

【讨论】:

    【解决方案2】:

    你想要的没有特殊的关键字。但是您可以考虑一种有趣的方法,它不会编译:

    func isTheEarthFlat(withUserIQ userIQ: Int, completionHandler: (Bool) -> Void) {
        let result: Bool
        defer {
           completionHandler(result)
        }
        if userIQ > 10 {
            result = false
        }
    }
    

    这样就可以了,completionHandler 被强制调用:

    func isTheEarthFlat(withUserIQ userIQ: Int, completionHandler: (Bool) -> Void) {
        let result: Bool
        defer {
           completionHandler(result)
        }
        if userIQ > 10 {
            result = false
        } else {
            result = true
        }
    }
    

    不确定这是一个好的模式。

    【讨论】:

      【解决方案3】:

      这是我想到的一个有趣的技巧。您定义 GuarenteedExecutionGuarenteedExecutionResult 类型。

      GuarenteedExecution 是闭包的包装器,用于必须保证闭包执行的上下文中。

      GuarenteedExecutionResult 是执行GuarenteedExecution 的结果。诀窍是具有所需的功能,例如isTheEarthFlat,返回 GuarenteedExecutionResult。获得GuarenteedExecutionResult 实例的唯一方法是在GuarenteedExecution 上调用execute(argument:)。实际上,负责保证返回的类型检查器功能现在被用于保证GuarenteedExecution 的执行。

      struct GuarenteedExecutionResult<R> {
          let result: R
      
          fileprivate init(result: R) { self.result = result }
      }
      
      struct GuarenteedExecution<A, R> {
          typealias Closure = (A) -> R
      
          let closure: Closure
      
          init(ofClosure closure: @escaping Closure) {
              self.closure = closure
          }
      
          func execute(argument: A) -> GuarenteedExecutionResult<R> {
              let result = closure(argument)
              return GuarenteedExecutionResult(result: result)
          }
      }
      

      示例用法,在一个单独的文件中(以便无法访问GuarenteedExecutionResult.init):

      let guarenteedExecutionClosure = GuarenteedExecution(ofClosure: {
          print("This must be called!")
      })
      
      func doSomething(guarenteedCallback: GuarenteedExecution<(), ()>)
          -> GuarenteedExecutionResult<()> {
          print("Did something")
          return guarenteedCallback.execute(argument: ())
      }
      
      _ = doSomething(guarenteedCallback: guarenteedExecutionClosure)
      

      【讨论】:

      • 有趣的想法,唯一的问题是你必须为你想要的每一个参数定义一个不同的GuaranteedExecution。例如如果闭包是(A, B) -&gt; R(A, B, C) -&gt; R等类型的呢?
      • @Paolo 是的,但您可以只使用元组。 ((X, Y, Z)) -&gt; R) 匹配 (A) -&gt; R,其中 A 是 (X, Y, Z)
      猜你喜欢
      • 2016-06-23
      • 1970-01-01
      • 2015-08-21
      • 1970-01-01
      • 2017-10-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多