【问题标题】:Swift: reusing a closure definition (with typealias)Swift:重用闭包定义(使用 typealias)
【发布时间】:2015-03-04 10:25:06
【问题描述】:

我正在尝试创建一些我将在我的 iOS 应用程序中大量使用的闭包定义。所以我想使用类型别名,因为它似乎是最有前途的......

我做了一个小 Playground 示例,详细说明了我的问题

// Here are two tries for the Closure I need
typealias AnonymousCheck = (Int) -> Bool
typealias NamedCheck = (number: Int) -> Bool

// This works fine
var var1: AnonymousCheck = {
    return $0 > 0
}
var1(-2)
var1(3343)

// This works fine
var var2: NamedCheck = {
    return $0 > 0
}
var2(number: -2)
var2(number: 12)

// But I want to use the typealias mainly as function parameter!
// So:

// Use typealias as function parameter
func NamedFunction(closure: NamedCheck) {
    closure(number: 3)
}
func AnonymousFunction(closure: AnonymousCheck) {
    closure(3)
}

// This works as well
// But why write again the typealias declaration?
AnonymousFunction({(another: Int) -> Bool in return another < 0})
NamedFunction({(another: Int) -> Bool in return another < 0})

// This is what I want... which doesn't work
// ERROR: Use of unresolved identifier 'number'
NamedFunction({NamedCheck in return number < 0})

// Not even these work
// ERROR for both: Anonymous closure arguments cannot be used inside a closure that has exlicit arguments
NamedFunction({NamedCheck in return $0 < 0})
AnonymousFunction({AnonymousCheck in return $0 < 0})

是我遗漏了什么还是 Swift 不支持它? 谢谢

编辑/添加:

以上只是一个简单的例子。在现实生活中,我的 typealias 更复杂。比如:

typealias RealLifeClosure = (number: Int, factor: NSDecimalNumber!, key: String, upperCase: Bool) -> NSAttributedString

我基本上想使用 typealias 作为快捷方式,所以我不必输入那么多。也许 typealias 不是正确的选择……还有其他的吗?

【问题讨论】:

    标签: swift closures type-alias


    【解决方案1】:

    您不是在此代码中重写 typealias 声明,而是在声明参数和返回类型:

    AnonymousFunction({(another: Int) -> Bool in return another < 0})
    

    令人高兴的是,Swift 的类型推断允许您使用以下任何一种 - 选择最适合您的样式:

    AnonymousFunction( { (number: Int) -> Bool in number < 0 } )
    AnonymousFunction { (number: Int) -> Bool in number < 0 }
    AnonymousFunction { (number) -> Bool in number < 0 }
    AnonymousFunction { number -> Bool in number < 0 }
    AnonymousFunction { number in number < 0 }
    AnonymousFunction { $0 < 0 }
    

    【讨论】:

    • 是的,我正在声明参数和返回类型。但如果我可以使用我已经定义的类型别名来做到这一点,那就太好了。这就是我基本上追求的......
    • 把你的类型别名想象成一个 type - 它不能成为该闭包范围内的实际参数,它只定义它们是什么。这是let number: Int = 3numberInt之间的区别。
    • 好的...我明白你的意思。不过,请查看我对问题的编辑。我只是想我可以使用 typealias 来避免我一直键入复杂的闭包。但我好像做不到。
    • 我明白你想要做的事情,但我认为你能得到的最好的结果是避免给出参数的类型——你无法避免命名它们.但由于这些类型更令人痛苦/混乱(我认为输入 { number, factor, key in ... } 可能比依赖其他地方定义的名称更合理)它还不错。
    • 正是@AirspeedVelocity 所说的。并且不要忘记,如果您不需要特定参数,可以用下划线忽略它:{ number, _, _, _ in ... }
    【解决方案2】:

    我不认为你能做你想做的事。为了稍微简化您的示例,您可以这样做:

    typealias NamedCheck = (number: Int) -> Bool
    let f: NamedCheck = { $0 < 5 }
    f(number: 1)
    NamedFunction(f)
    
    NamedFunction( { $0 < 5 } as NamedCheck)
    

    但是你不能做你想做的事,那就是依赖元组 arg 被称为 number 在闭包内引用它而不将它作为闭包的一部分:

    // compiler error, no idea what "number" is
    let g: NamedCheck = { number < 5 }
    

    请记住,您可以在不指定类型的情况下命名参数(从g 的类型推断):

    let g: NamedCheck = { number in number < 5 }
    

    而且,你可以随意命名:

    let h: NamedCheck = { whatevs in whatevs < 5 }
    NamedFunction(h)
    

    这是我认为正在发生的事情(这部分是猜测)。记住函数如何具有外部和内部参数名称:

    func takesNamedArgument(#namedArg: Int) { etc... }
    

    或者,直接写成:

    func takesNamedArgument(namedArg namedArg: Int) { etc... }
    

    但是你也可以给第二个,内部的,任何你喜欢的名字:

    func takesNamedArgument(namedArg whatevs: Int) { etc... }
    

    我认为这就是带有命名元组的闭包所发生的事情。 “外部”名称是“数字”,但您也必须给它一个“内部”名称,这是您必须在函数体中使用的名称。您不能在函数中使用外部参数。在闭包表达式的情况下,如果您不提供内部名称,则可以使用$0 等,但您不能跳过它,就像您可以完全跳过内部名称而只依赖外部名称一样定义常规函数时。

    我希望我可以通过以下方式证明这个理论:

    let f = { (#a: Int, #b: Int)->Bool in a < b }
    

    导致f 的类型为(a: Int, b: Int)-&gt;Bool)。这样编译:

    let g = { (number1 a: Int, number2 b: Int)->Bool in a < b }
    

    但它看起来不像参数的外部名称使其成为fg 的类型。

    【讨论】:

      【解决方案3】:

      创建闭包的语法是:

      { (parameters) -> return type in
          statements
      }
      

      in 左边是闭包签名(参数和返回值)。在某些情况下,当类型推断能够确定参数的数量及其类型以及返回值时,可以省略或简化签名。

      在您的情况下,它不起作用,因为您正在传递类型别名,但它被解释为参数名称。如果您满足以下条件,则这 3 行有效:

      1. 正确命名参数

        NamedFunction({number in return number < 0})
        AnonymousFunction({number in  return number < 0})
        
      2. 使用速记参数:

        NamedFunction({ return $0 < 0})
        AnonymousFunction({ return $0 < 0})
        
      3. 使用速记参数和隐式返回:

        NamedFunction({ $0 < 0})
        AnonymousFunction({ $0 < 0})
        

      【讨论】:

      • 谢谢安东尼奥。我喜欢使用 typealias 来缩短我的打字时间。现实生活中的闭包比示例中的要复杂得多。请参阅我对原始问题的编辑/添加...
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-07
      • 1970-01-01
      • 1970-01-01
      • 2019-02-13
      • 1970-01-01
      相关资源
      最近更新 更多