Ios 为什么Swift4会抛出一系列UIButton!到[UIButton?]类型?
我今天遇到了一个奇怪的问题。请看下面的代码:Ios 为什么Swift4会抛出一系列UIButton!到[UIButton?]类型?,ios,swift,swift4,Ios,Swift,Swift4,我今天遇到了一个奇怪的问题。请看下面的代码: class A { var button1: UIButton! var button2: UIButton! func foo() { let array = [button1, button2] } } Xcode表示数组是[UIButton?]类型。出于某种原因,Swift4会播放ui按钮元素到ui按钮?。为什么?斯威夫特为了安全起见,假定它们是可选的,而不是在默认情况下展开它们,因为它们在
class A {
var button1: UIButton!
var button2: UIButton!
func foo() {
let array = [button1, button2]
}
}
Xcode表示
数组
是[UIButton?]
类型。出于某种原因,Swift4会播放ui按钮代码>元素到ui按钮?
。为什么?斯威夫特为了安全起见,假定它们是可选的,而不是在默认情况下展开它们,因为它们在技术上可以是nil
。如果您试图像这样显式地将它们标记为隐式展开
let array: [UIButton!] = [button1, button2]
您将收到以下错误:
错误:隐式展开的选项仅允许在顶层和作为函数结果
在这种情况下,如果您希望将它们展开,那么只需将其定义为
let array: [UIButton] = [button1, button2]
解释
ImplicitlyUnwrappedOptional
不是一个独特的类型,而是一个普通的可选的
,其属性声明其值可能会被隐式强制(基于):
但是,外观!在属性或变量的末尾,声明的类型不再表示该声明具有IUO类型;相反,它表示(1)声明具有可选类型,(2)声明具有一个属性,指示其值可能是隐式强制的。(没有人会书写或观察这个属性,但我们将其称为@(u autounwrapped))这样的声明从此被称为IUO声明
因此,当您使用此选项时:
let array = [button1, button2]
编译器将数组
类型派生为[UIButton?]
,因为按钮1
和按钮2
的类型是可选的
,而不是隐式的
(即使只有一个按钮是可选的,它也会派生可选类型)
阅读更多信息
旁注:
此行为实际上与数组
无关,在下面的示例中,按钮2
的类型将被派生为UIButton?
,即使存在代码>并且在按钮中设置了一个值
:
var button: UIButton! = UIButton()
func foo() {
let button2 = button // button2 will be an optional: UIButton?
}
解决方案
如果要获取未包装类型的数组,有两个选项:
首先,正如他在回答中所建议的,使用显式类型,而不是让斯威夫特派生:
let array: [UIButton] = [button1, button2]
但是,如果其中一个按钮可能包含nil
,则会导致意外发现nil
崩溃
虽然通过使用隐式展开的可选按钮而不是可选按钮(!
而不是?
),您声称这些按钮中永远不会有零,但我仍然更喜欢他在评论中建议的第二个更安全的选项。即使用flatMap
(compactMap
在Swift 4+中),它将过滤掉nil
s(如果有),并返回一个未包装类型的数组:
let array = [button1, button2].flatMap { $0 }
因为ui按钮
不是一种类型,或者更确切地说,它是一种带有某些约定的UIButton?
。代码>表示始终隐式展开可选的。以下
var x: UIButton!
// Later
x.label = "foo"
语法上的糖是什么
var x: UIButton?
// Later
x!.label = "foo"
当您创建它们的数组时。编译器可以选择隐式展开它们并推断[UIButton]
,或者将它们保留为可选并推断[UIButton?]
。这是两种选择中比较安全的一种 我发现Swift博客确实是这些变化的最佳来源,因此:
在Swift 4之前:
许多人对于隐式展开选项的心理模型是,它们是一种类型,不同于常规选项。在Swift 3中,这正是它们的工作方式:像var a:Int这样的声明?将导致一个可选的类型,以及像var b:String这样的声明!将导致b具有隐式WrappedOptional类型
Swift 4
< IUOS的新心智模型是你考虑的一个模型!当家
同义词?它在声明中添加了一个标志
让编译器知道声明的值可以隐式
打开包装
换句话说,您可以读取字符串!as“此值的类型为
可选,还包含表示可以
如果需要,隐式展开”
此心智模型与新实现相匹配。无论你身在何处
T编译器现在将其视为具有类型T,并在中添加一个标志
其声明的内部表示形式允许类型检查器
知道它可以在必要时隐式展开值
因为按钮1
和按钮2
是可选的,所以所有引用都取自。Swift无法创建[UIButton!]
数组吗?为什么要使用强制展开代码>?您正在尝试创建非可选UIButton数组吗?通常使用展开代码>仅在初始化类时对象为零时使用,但以后肯定会初始化。@KamilHarasimowicz您不能有[UIButton!]
–根据,T代码>不再是独特的类型;现在它的类型与T?
相同,但它允许将属性附加到声明(例如属性)中,从而允许在某些情况下强制展开该属性。比较,&@Stephen让我们假设button1
和button2
是插座,foo()
函数将自定义它们。我不是这篇文章的作者,但最后一句话对我说得很清楚:)谢谢,我认为第二段应该是var x:UIButton?
我说得对吗?完美的解释。谢谢。@KamilHarasimowicz很高兴能为您提供帮助。我想补充一点,如果您有一个可选数组,您可以使用array.flatMap{$0}
,它将成为一个非可选数组,其中nil
值已从数组中删除。@EmilioPelaez感谢您的改进建议,我在回答中详细阐述了这一点