请考虑以下代码:
typealias bar = ()->()
let foo:bar = { baz -> () in
print("foobar")
return ()
}
let baz = foo()
print("(baz)")
哪些输出:
福巴
((
现在,如果我们更改此行:
let foo:bar = { baz -> () in
对此:
let foo:bar = { baz in
。然后什么也没发生。即,-> ()
由编译器推断。(注意:如果我们省略return ()
编译器也很高兴。
但是,与此同时,如果我们更改此行:
typealias bar = ()->()
对此:
typealias bar = ()
。然后编译器吓坏了,自杀了。为什么?
这似乎与 Swift 的文档相冲突,后者将()
(又名Void
(定义为:
未显式指定返回类型的函数的返回类型
根据该语句,由于typealias bar = ()->()
显式指定了返回类型,因此根据定义,它不能返回()
- 但它确实如此!这是完全不合逻辑和自相矛盾的。
有人可以向我解释一下他们对此的想法吗?这里的理由是什么?
如果->()
应该总是被推断出来,那么为什么还要有它呢?为什么不直接说()
总是一个函数,这样()()
总是返回()
?
如果我没记错的话,这就是你所说的情况:
typealias bar = ()
let foo: bar = { baz -> () in
print("foobar")
return ()
}
赋值的右手是类型() -> ()
的闭包,它是未计算的。foo
应该有类型 bar (()
(,但只有在评估闭包时才会出现这种情况,例如:
let foo: bar = { baz -> () in
print("foobar")
return ()
}()
我喜欢亚历山大的回答,但这里有另一个角度可能会有所帮助。
使用typealias bar = ...
声明,您将定义一个类型。该定义必须是完整和明确的。否则,类型系统将无法检查稍后声明的内容是否为该类型的成员。此声明提供了类型的完整签名 -- 对于函数/闭包类型,这意味着它的参数类型集和返回类型,即使两者都是Void
(也称为()
(。
使用let foo: bar = ...
,您将声明一个值并声明它必须是该类型的成员。由于类型定义的部分是已知的(由于类型别名(,因此在声明类型的成员时无需重复它们:
- 声明其返回类型,因为这是闭包符合的函数类型的一部分。您可以在闭包中
return
预期类型的任何值。(或者,由于您的函数类型定义了返回类型Void
,因此您不能return
任何内容。 - 您的闭包不需要声明其参数的类型,因为类型别名也已经这样做了。(或者,由于唯一参数的类型是
Void
,因此根本不需要声明参数。
在定义类型已知的闭包时,省略闭包语法是 Swift 的类型推断功能之一。你可以认为它等同于,如果你有一个enum Foo { case one, two, three }
和一个func bar(_ foo: Foo)
,你被允许在通话中只传递.one
(bar(.one)
(。Foo.one
是该常量的完全限定名称,但只要.one
就足够了,因为 Swift 可以推断出Foo
类型。