为什么Swift4会抛出一组UIButton!到[UIButton?]类型?

Kam*_*icz 14 ios swift swift4

我今天遇到了一个奇怪的问题.请看这段代码:

class A {

    var button1: UIButton!
    var button2: UIButton!

    func foo() {
        let array = [button1, button2]
    }
}
Run Code Online (Sandbox Code Playgroud)

Xcode说这array[UIButton?]类型.出于某种原因,Swift4将UIButton!元素转换为UIButton?.为什么?

Mil*_*sáľ 15

说明

ImplicitlyUnwrappedOptional不是一个独特的类型,而是Optional具有属性的法线,可以隐式强制声明其值(基于SE-0054):

但是,外观!在属性或变量声明的类型末尾不再表示声明具有IUO类型; 相反,它表示(1)声明具有可选类型,(2)声明具有一个属性,指示可以隐式强制其值.(没有人会写或观察这个属性,但我们会将其称为@_autounwrapped.)此类声明此后称为IUO声明.

因此,当你使用这个:

let array = [button1, button2]
Run Code Online (Sandbox Code Playgroud)

编译器派生的array类型[UIButton?],因为类型button1button2Optional<UIButton>,没有ImplicitlyUnwrappedOptional<UIButton>(哪怕只有一个按钮是可选的,它会得出可选类型).

阅读SE-0054的更多内容.

边注:

这个行为并不真正相关arrays,在下面的例子中button2,UIButton?即使存在!并且有一个值设置,也会导出它的类型button:

var button: UIButton! = UIButton()

func foo() {
    let button2 = button // button2 will be an optional: UIButton?
}
Run Code Online (Sandbox Code Playgroud)

如果您想获得一个未包装类型的数组,您有两个选择:

首先,正如Guy Kogus在他的回答中建议的那样,使用显式类型而不是让swift派生它:

let array: [UIButton] = [button1, button2]
Run Code Online (Sandbox Code Playgroud)

但是,如果其中一个按钮包含nil,则会导致Unexpectedly found nil崩溃.

虽然使用隐式展开的可选而不是可选(!而不是?)你声称这些按钮中永远不会有nil,但我仍然更喜欢EmilioPelaez在他的评论中提出的第二个更安全的选项.那就是使用(在Swift 4+中)将过滤掉s,如果有的话,并将返回一个unwrapped类型的数组:flatMapcompactMapnil

let array = [button1, button2].flatMap { $0 }
Run Code Online (Sandbox Code Playgroud)


Jer*_*myP 7

因为UIButton!它不是一种类型,或者更确切地说它是一种UIButton?约定.该!方法总是隐式展开可选项.下列

 var x: UIButton!
 // Later
 x.label = "foo"
Run Code Online (Sandbox Code Playgroud)

是语法糖

 var x: UIButton?
 // Later
 x!.label = "foo"
Run Code Online (Sandbox Code Playgroud)

当您创建它们的数组时.编译器可以选择隐式解包它们并推断[UIButton]或将它们保留为可选和推断[UIButton?].它是两个选项更安全的选择.