返回

Swift 5.3 新特性解析:多尾闭包,一个自相矛盾的特性

IOS

多尾闭包:Swift 中备受争议的特性

精简与混乱:争议之根源

在 Swift 5.3 之前,使用带有单个尾闭包的函数时,可以省略尾闭包标签,并在圆括号之外直接书写尾闭包。这种语法简洁明了,提高了代码的可读性。

然而,当涉及多个尾闭包时,Swift 5.3 中的多尾闭包特性引起了争议。它允许将多个尾闭包传递给一个函数,同时省略尾闭包标签,如下所示:

func doSomething(withClosures: ((Int) -> Void, (String) -> Void)) {
    // ...
}

// 新写法
doSomething { (number: Int), (text: String) in
    // ...
}

虽然这种语法保持了精简性,但它也带来了混乱和歧义。由于缺少尾闭包标签,很难识别每个闭包的作用和传入的类型,这会导致代码难以阅读和维护。

自相矛盾的特性

多尾闭包特性的自相矛盾之处在于,它试图在精简性与清晰性之间取得平衡。虽然它确实简化了某些场景的代码,但它却以牺牲可读性和可维护性为代价。

当函数具有多个参数类型时,省略尾闭包标签会导致代码中出现歧义。例如,考虑以下函数:

func doSomething(withParameters: (Int, String)) {
    // ...
}

使用多尾闭包语法时,这段代码可以这样编写:

doSomething { number, text in
    // ...
}

在这种情况下,numbertext 变量的类型不明确,这可能导致混乱和潜在的错误。

最佳实践

考虑到多尾闭包的争议性质,建议在使用时遵循以下最佳实践:

  • 避免使用多尾闭包,特别是当函数具有多个参数类型时。
  • 如果必须使用多尾闭包,请提供清晰的注释或文档来解释每个闭包的作用。
  • 考虑使用命名闭包,它可以提供更好的可读性和可维护性。

命名闭包允许为每个闭包指定一个名称,使其在代码中更容易识别和理解。例如,上述代码可以用命名闭包重写如下:

func doSomething(withClosures: ((Int) -> Void, (String) -> Void)) {
    // ...
}

// 使用命名闭包
doSomething {
    (number: Int) in
    // ...
}.{
    (text: String) in
    // ...
}

代码示例

// 精简语法
func doSomething(withClosure: (Int) -> Void) {
    // ...
}

doSomething { number in
    // ...
}

// 多尾闭包语法
func doSomething(withClosures: ((Int) -> Void, (String) -> Void)) {
    // ...
}

doSomething { number, text in
    // ...
}

// 命名闭包语法
func doSomething(withClosures: ((Int) -> Void, (String) -> Void)) {
    // ...
}

doSomething {
    (number: Int) in
    // ...
}.{
    (text: String) in
    // ...
}

常见问题解答

  1. 为什么多尾闭包引起了争议?

    争议源于多尾闭包语法带来的精简性和混乱之间的权衡。

  2. 什么时候应该使用多尾闭包?

    建议避免使用多尾闭包,特别是当函数具有多个参数类型时。

  3. 如何解决多尾闭包带来的歧义问题?

    使用命名闭包或提供清晰的注释或文档来解释每个闭包的作用。

  4. 多尾闭包与命名闭包有何区别?

    命名闭包允许为每个闭包指定一个名称,从而提高可读性和可维护性。

  5. 为什么使用多尾闭包时建议遵循最佳实践?

    最佳实践可以确保代码的可读性、可维护性,并避免潜在的错误。