使用 Swift 字符串的可选链接

Optional chaining with Swift strings

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

var s: String?

s 可能包含 nil,或者包含在 Optional 中的字符串。所以,我试过这个来获得它的长度:

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

然而,编译器想要这样:

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

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

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

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应该是非法的。

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

当你说:

My understanding of optional chaining is that, once you start using ?. in a dotted expression, the rest of the properties are made optional and are typically accessed by ?., not ..

我会说你快到了。

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

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

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


已编辑以下评论

来自评论:

I still find it strange that both s?.characters.count and (s?.characters)?.count compile, but (s?.characters).count doesn't. Why is there a difference between the first and the last expression?

我会尽量在这里回答,这里比评论区有更多空间:

s?.characters.count

如果 s 为 nil,则整个表达式 return 为 nil,否则为 Int。所以 return 类型是 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)

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

If a postfix expression that contains an optional-chaining expression is nested inside other postfix expressions, only the outermost expression returns an optional type.

继续示例:

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

这解释了为什么编译器需要 s?.characters.count 在我上面的例子中,我认为它回答了最初的问题。然而,正如@Martin R 在评论中观察到的那样,编译器为何对这两个表达式进行不同处理仍然是个谜:

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

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

(s?.characters) 

是"nested inside"整体后缀表达式

(s?.characters).count

因此应与未加括号的版本一样对待。但这是一个单独的问题。

感谢大家的贡献!