Дополнительная цепочка со строками Swift

С дополнительной цепочкой, если у меня есть переменная Swift

var s: String?

s может содержать nil или 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 действительно является необязательным экземпляром, указывающим, что s?.characters.count должен быть незаконным.

Может кто-нибудь помочь мне понять это положение вещей?

Ответы

Ответ 1

Когда вы говорите:

Мое понимание необязательной цепочки состоит в том, что после того, как вы начнете использовать ?. в точечном выражении, остальные свойства становятся необязательными и к ним обычно обращаются ?., а не ..

Я бы сказал, что вы почти там.

Не то, чтобы все свойства были сделаны необязательными, так что исходный вызов является необязательным, поэтому он выглядит так, как другие свойства являются необязательными.

characters не является необязательным свойством и не является count, но значение, которое вы вызываете, является необязательным. Если есть значение, то свойства characters и count возвращают значение; в противном случае возвращается 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

Прервать это: если s - nil, то (s?.characters) - nil, поэтому мы не можем называть его count.

Чтобы вызвать свойство count на (s?.characters), выражение нужно по желанию распаковать, то есть записать как:

(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)

Ответ 2

В списке рассылки Swift-пользователей Ingo Maier был достаточно любезен, чтобы указать мне на раздел дополнительных выражений цепочки в спецификации языка Swift, который гласит:

Если выражение postfix, которое содержит необязательно-цепное выражение, вложено внутри других постфиксных выражений, только внешнее выражение возвращает необязательный тип.

Продолжается пример:

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

и, следовательно, его следует рассматривать так же, как версия, не заключенная в скобки. Но это отдельная проблема.

Спасибо всем за вклад!