【问题标题】:Optional chaining with Swift strings使用 Swift 字符串的可选链接
【发布时间】:2016-08-01 13:06:31
【问题描述】:

如果我有一个 Swift 变量,则使用可选链接

var s: String?

s 可能包含 nil,或者包裹在 Optional 中的 String。所以,我尝试了这个来获得它的长度:

let count = s?.characters?.count ?? 0

但是,编译器想要这样:

let count = s?.characters.count ?? 0

我对可选链接的理解是,一旦您开始在点分表达式中使用?.,其余属性将变为可选,并且通常由?. 访问,而不是.

所以,我进一步挖掘并在操场上尝试了这个:

var s: String? = "Foo"
print(s?.characters)
// Output: Optional(Swift.String.CharacterView(_core: Swift._StringCore(_baseAddress: 0x00000001145e893f, _countAndFlags: 3, _owner: nil)))

结果表明s?.characters确实是一个Optional实例,说明s?.characters.count应该是非法的。

有人可以帮我理解这种情况吗?

【问题讨论】:

  • s?.characters.count(s?.characters)?.count 都编译,但 (s?.characters).count 不编译。看起来characters.count 在第一种情况下被视为单个可选链接,但我还没有看到记录。
  • 现在也在 [swift-users] 上讨论:lists.swift.org/pipermail/swift-users/Week-of-Mon-20160801/…

标签: swift string optional


【解决方案1】:

当你说:

我对可选链的理解是,一旦您开始在点分表达式中使用?.,其余属性将变为可选并且通常由?. 访问,而不是.

我会说你快到了。

并不是所有的属性都是可选的,而是原来的调用是可选的,所以看起来其他的属性都是可选的。

characters 不是可选属性,count 也不是,但您调用它的值是可选的。如果有值,则characterscount 属性将返回一个值;否则,返回nil。正因为如此,s?.characters.count 的结果返回了一个Int?

如果其中任何一个属性是可选的,那么您需要将? 添加到它,但在您的情况下,它们不是。所以你不会。


已编辑以下评论

来自评论:

我仍然觉得奇怪的是 s?.characters.count(s?.characters)?.count 都编译,但 (s?.characters).count 没有。为什么第一个表达式和最后一个表达式之间存在差异?

我会在这里尝试回答,那里的空间比评论区更多:

s?.characters.count

如果s 为nil,则整个表达式返回nil,否则返回Int。所以返回类型是Int?

(s?.characters).count // Won’t compile

分解:如果snil,那么(s?.characters)nil,所以我们不能调用count

为了调用(s?.characters) 上的count 属性,表达式需要选择性地展开,即写为:

(s?.characters)?.count

已编辑以进一步添加

我能解释这一点的最好方法是用这个操场代码:

let s: String? = "hello"

s?.characters.count
(s?.characters)?.count
(s)?.characters.count
((s)?.characters)?.count

// s?.characters.count
func method1(s: String?) -> Int? {
    guard let s = s else { return nil }

    return s.characters.count
}

// (s?.characters).count
func method2(s: String?) -> Int? {
    guard let c = s?.characters else { return nil }

    return c.count
}

method1(s)
method2(s)

【讨论】:

  • 我仍然觉得奇怪的是 s?.characters.count(s?.characters)?.count 编译,但 (s?.characters).count 没有。为什么第一个和最后一个表达式有区别?
  • (s?.characters).count 中,您尝试获取可选的count 属性。在第一个 characters 不是可选的。
  • @MartinR 我试图在编辑后的答案中回答这个问题。
  • 但是在第一个表达式中不是同样的参数吗? s?.characters 具有 Int? 类型(如果 s == nil 则为 nil)。为什么我们可以在s?.characters.count 中调用count?为什么添加括号会有所不同?
  • s 具有可选类型。成功解包后,您可以将所有其他调用链接到非可选值。所以你在String 上调用characters.count(如果它存在),或者如果它不存在则只返回nil。当你用括号括起来时,你是在调用外部方法之前评估括号。因此,不是在未包装的值上调用 characters.count,而是在可选字符串(这是一个可选的)上调用 characters,然后对必须有条件地展开的结果调用 count。
【解决方案2】:

在 Swift 用户邮件列表中,Ingo Maier 非常友好地向我指出了 Swift 语言规范中的 section on optional chaining expressions,其中指出:

如果包含可选链表达式的后缀表达式嵌套在其他后缀表达式中,则只有最外层的表达式返回可选类型。

继续举例:

var c: SomeClass?
var result: Bool? = c?.property.performAction()

这解释了为什么编译器在上面的示例中需要 s?.characters.count,我认为它回答了原始问题。然而,正如@Martin R 在评论中所观察到的,为什么编译器会以不同方式处理这两个表达式仍然是个谜:

s?.characters.count
(s?.characters).count

如果我正确阅读规范,子表达式

(s?.characters) 

“嵌套在”整个后缀表达式中

(s?.characters).count

因此应该被视为与非括号版本相同。但这是一个单独的问题。

感谢大家的贡献!

【讨论】:

  • 正如列表中所讨论的,规范对问号的范围有点缺乏,但我认为 swiftc 做的事情是正确的。在编程语言中使用括号来界定各种运算符/关键字的范围是相当普遍的。它通常会导致很好的(我会说是直观的)属性,您可以用 let 绑定替换带括号的表达式,并且仍然具有相同的语义。使用您的示例,let x = s?.characters; x?.count(s?.characters)?.count 具有相同的语义,如果 (s?.characters).count 是有效的 Swift 代码,则不会出现这种情况。
猜你喜欢
  • 2023-03-24
  • 2015-10-28
  • 1970-01-01
  • 1970-01-01
  • 2020-12-10
  • 2016-04-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多