Новый учебник SwiftUI имеет следующий код:
struct ContentView: View {
var body: some View {
Text("Hello World")
}
}
Во второй строке слово some
, а на их сайте подсвечивается как бы ключевое слово.
Swift 5.1, похоже, не имеет some
в качестве ключевого слова, и я не вижу, что еще some
может там делать слово , так как оно идет туда, где обычно идет тип. Есть ли новая, необъявленная версия Swift? Это функция, которая используется в типе способом, о котором я не знал?
Что делает ключевое слово some
?
Ответы:
some View
является непрозрачным типом результата , как введенный SE-0244 и доступно в Swift 5.1 с Xcode 11. Вы можете думать об этом как «обратном» общем заполнителе.В отличие от обычного родового заполнителя, который удовлетворяет вызывающая сторона:
Непрозрачный тип результата - это неявный универсальный заполнитель, удовлетворяемый реализацией , поэтому вы можете подумать об этом:
как выглядит так:
На самом деле, конечная цель этой функции - разрешить обратные обобщения в этой более явной форме, что также позволит вам добавить ограничения, например
-> <T : Collection> T where T.Element == Int
.Смотрите этот пост для получения дополнительной информации .Главное , чтобы забрать из этого является то, что функция , возвращающая
some P
одно , что возвращает значение определенного одного типа бетона , который соответствуетP
. Попытка вернуть различные соответствующие типы внутри функции приводит к ошибке компилятора:Поскольку неявный родовой заполнитель не может быть удовлетворен несколькими типами.
Это в отличие от функции возврата
P
, которая может использоваться для представления обоихS1
иS2
потому что она представляет произвольноеP
соответствующее значение:Итак, какие преимущества имеют непрозрачные типы результатов по
-> some P
сравнению с типами возвращаемых протоколов-> P
?1. Непрозрачные типы результатов могут использоваться с PAT
Основное ограничение протоколов в настоящее время заключается в том, что PAT (протоколы со связанными типами) не могут использоваться в качестве реальных типов. Хотя это ограничение, вероятно, будет снято в будущей версии языка, поскольку непрозрачные типы результатов являются просто общими заполнителями, их можно использовать с PAT сегодня.
Это означает, что вы можете делать такие вещи, как:
2. Непрозрачные типы результатов имеют идентичность
Поскольку непрозрачные типы результатов обеспечивают возвращение одного конкретного типа, компилятор знает, что два вызова одной и той же функции должны возвращать два значения одного и того же типа.
Это означает, что вы можете делать такие вещи, как:
Это допустимо, потому что компилятор знает, что оба
x
иy
имеют один и тот же конкретный тип. Это важное требование для==
, где оба параметра типаSelf
.Это означает, что он ожидает два значения, которые оба имеют тот же тип, что и конкретный соответствующий тип. Даже если бы
Equatable
его можно было использовать как тип, вы не смогли бы сравнить два произвольныхEquatable
соответствующих значения друг с другом, например:Поскольку компилятор не может доказать, что два произвольных
Equatable
значения имеют один и тот же базовый конкретный тип.Аналогичным образом, если мы ввели другую непрозрачную функцию возврата типа:
Пример становится незаконным , потому что хотя оба
foo
иbar
возвращениеsome Equatable
, их «обратным» общих заполнителиOutput1
иOutput2
может быть удовлетворены различными типами.3. Непрозрачные типы результатов сочетаются с общими заполнителями
В отличие от обычных значений, типизированных протоколом, непрозрачные типы результатов хорошо сочетаются с обычными общими заполнителями, например:
Это не сработало бы, если
makeP
бы только что вернулосьP
, поскольку дваP
значения могут иметь разные базовые конкретные типы, например:Зачем использовать непрозрачный тип результата поверх конкретного типа?
В этот момент вы можете подумать, почему бы просто не написать код:
Что ж, использование непрозрачного типа результата позволяет сделать тип
S
детализацией реализации, предоставляя только интерфейс, предоставленныйP
, предоставляя гибкость в изменении конкретного типа в дальнейшем без нарушения кода, который зависит от функции.Например, вы можете заменить:
с участием:
не нарушая любой код, который вызывает
makeP()
.См. Раздел «Непрозрачные типы» в руководстве по языку и предложении Swift Evolution для получения дополнительной информации об этой функции.
источник
return
не требуется в функциях с одним выражениемfunc makeP() -> some P
иfunc makeP() -> P
? Я прочитал предложение и не вижу этой разницы для своих образцов тоже.some P
было бы необходимоДругой ответ хорошо объясняет технический аспект нового
some
ключевого слова, но этот ответ попытается легко объяснить почему .Допустим, у меня есть протокол Animal, и я хочу сравнить, являются ли два животных братьями и сестрами:
Таким образом, имеет смысл сравнивать только двух братьев и сестер, если они одного типа .
Теперь позвольте мне создать пример животного только для справки.
Путь без
some T
Теперь допустим, что у меня есть функция, которая возвращает животное из «семьи».
Теперь возникает проблема, если я попытаюсь сделать это:
Это выдаст ошибку .
Зачем? Причина в том, что когда вы звоните,
animal1.isSibling(animal2)
Свифт не знает, животные ли это собаки, кошки или что-то еще. Насколько Свифт знает,animal1
и этоanimal2
могут быть не связанные виды животных . Так как мы не можем сравнивать животных разных типов (см. Выше). Это будет ошибкаКак
some T
решается эта проблемаДавайте перепишем предыдущую функцию:
animal1
иanimal2
это неAnimal
, но они относятся к классу , который реализует животных .То, что это позволяет вам делать сейчас, это то, что когда вы звоните
animal1.isSibling(animal2)
, Swift знает этоanimal1
иanimal2
относится к тому же типу.Итак, как мне нравится думать об этом:
(Отказ от ответственности за саморекламу) Я написал пост в блоге , в котором подробно рассказывается об этой новой функции (тот же пример, что и здесь)
источник
some
в return type работает как ограничение на тело функции. Поэтомуsome
требуется возвращать только один конкретный тип во всем теле функции. Например: если есть,return randomDog
все остальные возвраты должны работать только сDog
. Все преимущества проистекают из этого ограничения: доступностьanimal1.isSibling(animal2)
и польза от компиляцииfunc animalFromAnimalFamily() -> some Animal
(потому что теперь онаSelf
определяется под капотом). Это правильно?Ответ Хэмиша довольно удивительный и отвечает на вопрос с технической точки зрения. Я хотел бы добавить некоторые соображения о том, почему ключевое слово
some
используется в этом конкретном месте в учебных пособиях Apple по SwiftUI, и почему это хорошая практика для подражания.some
не является требованием!Прежде всего, вам не нужно объявлять
body
возвращаемый тип как непрозрачный тип. Вы всегда можете вернуть конкретный тип вместо использованияsome View
.Это также скомпилируется. Когда вы посмотрите на
View
интерфейс, вы увидите, что возвращаемый типbody
является связанным типом:Это означает, что вы указываете этот тип, аннотируя
body
свойство конкретным типом по вашему выбору. Единственное требование - этот тип должен реализовывать самView
протокол.Это может быть конкретный тип, который реализует
View
, например,Text
Image
Circle
или непрозрачный тип, который реализует
View
, т.е.some View
Общие виды
Проблема возникает, когда мы пытаемся использовать представление стека в качестве
body
возвращаемого типа, напримерVStack
илиHStack
:Это не скомпилируется, и вы получите ошибку:
Это связано с тем, что представления стека в SwiftUI являются общими типами! 💡 (И то же самое верно для списков и других типов контейнерных представлений.)
Это имеет большой смысл, потому что вы можете подключить любое количество представлений любого типа (при условии, что это соответствует
View
протоколу). Конкретный типVStack
в теле выше на самом делеКогда мы позже решим добавить представление в стек, его конкретный тип изменится. Если мы добавим второй текст после первого, мы получим
Даже если мы сделаем небольшое изменение, такое же тонкое, как добавление разделителя между текстом и изображением, тип стека изменится:
Из того, что я могу сказать, именно поэтому Apple рекомендует в своих руководствах всегда использовать
some View
самый общий непрозрачный тип, которому удовлетворяют все представления, в качествеbody
возвращаемого типа. Вы можете изменить реализацию / макет своего пользовательского представления, не изменяя каждый раз тип возврата вручную.Дополнение:
Если вы хотите получить более понятное представление о непрозрачных типах результатов, я недавно опубликовал статью, которую стоит прочитать:
🔗 Что это за «некоторые» в SwiftUI?
источник
Я думаю, что все ответы на данный момент отсутствуют, что
some
полезно прежде всего в чем-то вроде DSL (предметно-ориентированного языка), такого как SwiftUI или библиотека / инфраструктура, которая будет иметь пользователей (другие программисты) будут отличаться от вас.Вы, вероятно, никогда не
some
будете использовать в своем обычном коде приложения, за исключением, возможно, того, что он может обернуть универсальный протокол, чтобы его можно было использовать как тип (а не просто как ограничение типа). То, чтоsome
нужно, это дать компилятору возможность узнать, что это за тип, что-то особенное, в то же время поместив перед ним фасад супертипа.Таким образом, в SwiftUI, где вы являетесь пользователем, все, что вам нужно знать, это то, что что-то есть
some View
, в то время как за кулисами может происходить всякое ханж-панки, от которого вы защищены. Этот объект на самом деле очень специфический тип, но вам никогда не придется слышать о том, что это такое. И все же, в отличие от протокола, это полноценный тип, потому что где бы он ни появлялся, это просто фасад для какого-то определенного полноценного типа.В будущей версии SwiftUI, где вы ожидаете
some View
, разработчики могут изменить базовый тип этого конкретного объекта. Но это не сломает ваш код, потому что он никогда не упоминал базовый тип.Таким образом,
some
фактически делает протокол больше похожим на суперкласс. Это почти реальный тип объекта, хотя и не совсем (например, объявление метода протокола не может вернуть asome
).Поэтому, если вы собираетесь использовать
some
что-либо, скорее всего, если бы вы писали DSL или фреймворк / библиотеку для использования другими, и вы хотели замаскировать детали базового типа. Это упростит ваш код для использования другими и позволит вам изменить детали реализации, не нарушая их код.Однако вы также можете использовать его в своем собственном коде для защиты одной области кода от деталей реализации, скрытых в другой области кода.
источник
some
Ключевое слово из Swift 5.1 ( скоро-эволюция предложение ) используются в сочетании с протоколом в качестве типа возвращаемого значения .Примечания к выпуску Xcode 11 представляют это так:
В приведенном выше примере вам не нужно указывать, что вы собираетесь вернуть
Array
. Это позволяет вам даже возвращать универсальный тип, который просто соответствуетCollection
.Обратите внимание также на эту возможную ошибку, с которой вы можете столкнуться:
Это означает, что вы должны использовать доступность, чтобы избежать
some
на iOS 12 и раньше:источник
some
на iOS 12 и раньше. Пока вы делаете, у вас все будет хорошо. Проблема только в том, что компилятор не предупреждает вас об этом.some
ключевое слово в данном примере кода в Swift 5.0 или Swift 4.2. Ошибка будет: « Протокол« Коллекция »может использоваться только в качестве общего ограничения, потому что он имеет Self или связанные требования типа »'some' означает непрозрачный тип. В SwiftUI View объявляется как протокол
Когда вы создаете свое представление как Struct, вы соглашаетесь с протоколом View и говорите, что тело var возвратит что-то, что будет подтверждать View Protocol. Это как общая абстракция протокола, где вам не нужно определять конкретный тип.
источник
Я попытаюсь ответить на это очень простым практическим примером (что это за непрозрачный тип результата )
Предполагая, что у вас есть протокол со связанным типом и две структуры, реализующие его:
До Swift 5.1 ниже недопустимо из-за
ProtocolWithAssociatedType can only be used as a generic constraint
ошибки:Но в Swift 5.1 это нормально (
some
добавлено):Выше приведено практическое использование, широко используемое в SwiftUI для
some View
.Но есть одно важное ограничение - возвращаемый тип необходимо знать во время компиляции, поэтому ниже не будет работать с
Function declares an opaque return type, but the return statements in its body do not have matching underlying types
ошибкой:источник
Простой случай использования, который приходит на ум, - написание универсальных функций для числовых типов.
источник
Для тех, у кого кружилась голова от темы, здесь очень расшифровка и пошаговая статья благодаря Вадиму Булавину.
https://www.vadimbulavin.com/opaque-return-types-and-the-some-keyword-in-swift/
источник