Swift 可选项 - 不一致性?

6
我有点困惑 - 我原以为理解了Optionals,但在苹果开发者论坛上,Chris L提到一个解决不可变Optional问题的方法是为Optional值类型创建一个类包装器。-链接

然而,以UIWindow为例(一种带有各种属性的可选类类型),这些属性仍然似乎无法通过可选链进行更改!

var window: UIWindow?

//this works (force unwrap)
self.window!.backgroundColor = UIColor.greenColor()


//this optional chain doesn't work... why not? Isn't this just a safer version of the above?
self.window?.backgroundColor = UIColor.redColor()

这个问题似乎在beta 5中已经被解决了!

2
可选链只能在操作数的右侧起作用,您不能选择链接一个对象并告诉它的属性必须是“this”。这就像将可能的nil指针设置为其他值一样 - 这是无效的操作数,而不是不一致性。 - holex
2个回答

5

可选链用于读取值(返回值或nil),也适用于调用方法(调用该方法或不执行任何操作),但不适用于赋值。

我相信这是有意设计的,但我不知道为什么会这样,因为这本质上与以下代码相同:

extension UIWindow {
    func setBackgroundColor(color: UIColor) {
        self.backgroundColor = color
    }
}

self.window?.setBackgroundColor(UIColor.redColor())

这个问题可以通过报告错误或在开发论坛上咨询来解决,并且可以正常工作。我认为可选链在这里逻辑上不一致。

你在开发论坛中提供的示例与此问题无关,因为该问题涉及值类型。


我认为这种错误不是逻辑上的问题,而是可选链的行为,不允许为可能为空的值设置任何值。 - holex
1
@holex 这与 setter 方法不一致(未声明为属性)。从技术上讲,行为是相同的。我看不出为什么应该禁止它。 - Sulthan
在我的逻辑中,这必须被禁止,因为 nil = UIColor.greenColor() 在任何层面上都是无效的操作数,可选值不能在左侧,只能在右侧。 - holex
你的例子不同,因为可选项在右侧,但是因为返回值是void且操作数的左侧不是必需的。 - holex
1
@Sulthan 在 Beta 5 中似乎已经被修复了! - Woodstock
显示剩余5条评论

5

更新 Xcode beta 5


Xcode beta 5 已解决了原先提出的问题,这可能使得本回答失效。


原始答案


这可能需要进一步解释,为什么它绝对不是不一致的行为而是简单的无效。

可选值必须始终位于实际操作数的右侧,不能位于其左侧。

通过两个简单的示例来查看该逻辑:

示例 1

对于以下代码行:

self.window?.backgroundColor = UIColor.redColor()
                      < LEFT ^ RIGHT >

可选项位于左侧,这意味着左侧可能为nil,在运行时以下操作数将出现在此处:
nil = UIColor.redColor()

这在任何层面上都是无效的,不需要进一步或复杂的解释 - 一个nil不能分配给其他东西,这就是编译器不允许的原因。

NOTE: you may assume that the logical behaviour would be like this in the case of self.window = nil:

nil.backgroundColor = UIColor.redColor()

but the documentation about Optional Chaining highlights a very important behaviour which explains why that is not happening at all:

Multiple queries can be chained together, and the entire chain fails gracefully if any link in the chain is nil.

重点在于单词"entire",因此左侧将是nil = ...不是nil.backgroundColor = ...,这与Objective-C不同。

例子2

另一个答案突出了另一种解决方法:

self.window?.setBackgroundColor(UIColor.redColor())

为什么这样做是有效的?这里不会有一点不一致吗?绝对不会。

实际上,这里的可选项位于操作数的右边,因为这行等于这行,但我们实际上并不关心在实践中得到void

let result: Void! = self.window?.setBackgroundColor(UIColor.redColor())
           < LEFT ^ RIGHT >

正如您所见,这里根本没有任何不一致的地方,因为在 self.window = nil 的情况下,该行在运行时将等同于上面解释中的这一行:

let result: Void! = nil

这将是一个完全合法的操作数。

逻辑很简单,可选项必须始终位于操作数(或运算符)的右侧,在左侧它只能是非可选的


谢谢您提供的信息。非常有趣的阅读 - 我认为我对问题有根本性的误解。无论如何,如果您所说的是真的(我相信是这样的),那么这种行为不会有任何改变...这意味着myArray?.removeAtIndex(0)(根据Apple Dev论坛上的讨论)永远不会是有效的Swift,对吗? - Woodstock
@JohnWoods,关于你提出的话题,我有一个想法。关于mutating方法的工作原理,目前很少有相关信息,因此我们可能无法确定直接回答你的问题。 - holex
@JohnWoods,即使可选项变成可变的,这也不会改变它们是“可选”的事实,从逻辑上讲,它们不能在“左侧”。然而,如果编译器将来变得更智能,有一天这样的操作数可能不再无效,但对我来说,如果链中的任何元素都为nil,抛弃整个链条似乎是个聪明(=更快)的解决方案 - 因此,我并不期望编译器会有这样的更新,因为一个安全的“运行时”解决方案相对较慢(有嵌套的if语句)。 - holex
似乎在Beta 5中已经修复了! - Woodstock
太棒了,既有带问号的赋值,又有可变值类型... 这是一个很好的改变!我认为带问号的赋值非常棒,我们可以像 Objective-C 一样有效地传递 nil。 - Woodstock
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接