Le nouveau tutoriel SwiftUI a le code suivant:
struct ContentView: View {
var body: some View {
Text("Hello World")
}
}
La deuxième ligne du mot some
, et sur leur site est mise en évidence comme s'il s'agissait d'un mot-clé.
Swift 5.1 ne semble pas avoir some
de mot-clé, et je ne vois pas ce que le mot some
pourrait faire d'autre, car il va là où le type va habituellement. Existe-t-il une nouvelle version inopinée de Swift? Est-ce une fonction qui est utilisée sur un type d'une manière que je ne connaissais pas?
Que fait le mot some
- clé ?
Réponses:
some View
est un type de résultat opaque introduit par SE-0244 et est disponible dans Swift 5.1 avec Xcode 11. Vous pouvez le considérer comme un espace réservé générique "inversé".Contrairement à un espace réservé générique régulier qui est satisfait par l'appelant:
Un type de résultat opaque est un espace réservé générique implicite satisfait par l' implémentation , vous pouvez donc y penser:
comme ressemblant à ceci:
En fait, l'objectif final de cette fonctionnalité est d'autoriser les génériques inversés sous cette forme plus explicite, ce qui vous permettrait également d'ajouter des contraintes, par exemple
-> <T : Collection> T where T.Element == Int
. Voir cet article pour plus d'informations .La principale chose à retenir est qu'une fonction renvoyant
some P
est une fonction qui renvoie une valeur d'un type concret unique conformeP
. Tenter de renvoyer différents types conformes dans la fonction génère une erreur de compilation:Comme l'espace réservé générique implicite ne peut pas être satisfait par plusieurs types.
Cela contraste avec une fonction renvoyant
P
, qui peut être utilisée pour représenter les deuxS1
etS2
parce qu'elle représente uneP
valeur de conformité arbitraire :D'accord, quels sont les avantages des types de résultats opaques par rapport aux types de
-> some P
retour de protocole-> P
?1. Les types de résultats opaques peuvent être utilisés avec les PAT
Une limitation actuelle majeure des protocoles est que les PAT (protocoles avec des types associés) ne peuvent pas être utilisés comme types réels. Bien qu'il s'agisse d'une restriction qui sera probablement levée dans une future version du langage, car les types de résultats opaques ne sont en fait que des espaces réservés génériques, ils peuvent être utilisés avec les PAT aujourd'hui.
Cela signifie que vous pouvez faire des choses comme:
2. Les types de résultats opaques ont une identité
Étant donné que les types de résultats opaques appliquent un seul type concret est renvoyé, le compilateur sait que deux appels à la même fonction doivent renvoyer deux valeurs du même type.
Cela signifie que vous pouvez faire des choses comme:
Ceci est légal car le compilateur sait que les deux
x
ety
ont le même type concret. Il s'agit d'une exigence importante pour==
, où les deux paramètres de typeSelf
.Cela signifie qu'il attend deux valeurs qui sont à la fois du même type que le type conforme au béton. Même s'ils
Equatable
étaient utilisables comme type, vous ne seriez pas en mesure de comparer deuxEquatable
valeurs conformes arbitraires , par exemple:Comme le compilateur ne peut pas prouver que deux
Equatable
valeurs arbitraires ont le même type concret sous-jacent.De manière similaire, si nous introduisions une autre fonction de retour de type opaque:
L'exemple devient illégale parce que même si les deux
foo
et lebar
retoursome Equatable
, leur « inverse » génériques des espaces réservésOutput1
etOutput2
pourrait être satisfaite par différents types.3. Les types de résultats opaques composent avec des espaces réservés génériques
Contrairement aux valeurs de type protocole standard, les types de résultats opaques se composent bien avec des espaces réservés génériques réguliers, par exemple:
Cela n'aurait pas fonctionné s'il
makeP
venait de revenirP
, car deuxP
valeurs peuvent avoir différents types concrets sous-jacents, par exemple:Pourquoi utiliser un type de résultat opaque sur le type de béton?
À ce stade, vous pensez peut-être à vous-même, pourquoi ne pas simplement écrire le code comme suit:
Eh bien, l'utilisation d'un type de résultat opaque vous permet de faire le type
S
un détail d'implémentation en exposant uniquement l'interface fournie parP
, ce qui vous donne la possibilité de changer le type concret plus tard sur la ligne sans casser le code qui dépend de la fonction.Par exemple, vous pouvez remplacer:
avec:
sans casser aucun code qui appelle
makeP()
.Voir la section Types opaques du guide de langue et la proposition d'évolution Swift pour plus d'informations sur cette fonctionnalité.
la source
return
n'est pas requis dans les fonctions à expression simplefunc makeP() -> some P
etfunc makeP() -> P
? J'ai lu la proposition et je ne vois pas non plus cette différence pour leurs échantillons.some P
serait nécessaireL'autre réponse explique bien l'aspect technique du nouveau
some
mot clé, mais cette réponse tentera d'expliquer facilement pourquoi .Disons que j'ai un protocole Animal et que je veux comparer si deux animaux sont frères et sœurs:
De cette façon, il est logique de comparer si deux animaux sont frères et sœurs s'ils sont du même type d'animal.
Maintenant, permettez-moi de créer un exemple d'un animal juste pour référence
Le chemin sans
some T
Supposons maintenant que j'ai une fonction qui renvoie un animal d'une «famille».
Maintenant, le problème vient si j'essaye de faire ceci:
Cela générera une erreur .
Pourquoi? Eh bien, la raison en est que lorsque vous appelez
animal1.isSibling(animal2)
Swift, il ne sait pas si les animaux sont des chiens, des chats ou autre. Pour autant que Swift le sache,animal1
etanimal2
pourrait être une espèce animale non apparentée . Puisque nous ne pouvons pas comparer des animaux de différents types (voir ci-dessus). Ce sera une erreurComment
some T
résout ce problèmeRéécrivons la fonction précédente:
animal1
et ne leanimal2
sont pasAnimal
, mais ce sont des classes qui implémentent Animal .Ce que cela vous permet de faire maintenant, c'est lorsque vous appelez
animal1.isSibling(animal2)
, Swift le saitanimal1
etanimal2
est du même type.Donc, la façon dont j'aime y penser:
(Avis de non-promotion) J'ai écrit un article de blog qui va un peu plus en profondeur (même exemple qu'ici) sur cette nouvelle fonctionnalité
la source
some
dans les retours, le type fonctionne comme une contrainte au corps de la fonction.some
Exige donc de renvoyer un seul type concret dans le corps de fonction entier. Par exemple: s'il y en a,return randomDog
tous les autres retours doivent fonctionner uniquement avecDog
. Tous les avantages proviennent de cette contrainte: disponibilitéanimal1.isSibling(animal2)
et bénéfice de la compilation defunc animalFromAnimalFamily() -> some Animal
(carSelf
se définit désormais sous le capot). Est-ce correct?La réponse de Hamish est assez impressionnante et répond à la question d'un point de vue technique. Je voudrais ajouter quelques réflexions sur la raison pour laquelle le mot
some
- clé est utilisé à cet endroit particulier dans les didacticiels SwiftUI d'Apple et pourquoi c'est une bonne pratique à suivre.some
n'est pas une exigence!Tout d'abord, vous n'avez pas besoin de déclarer le
body
type de retour de comme un type opaque. Vous pouvez toujours renvoyer le type concret au lieu d'utiliser lesome View
.Cela compilera également. Lorsque vous regardez dans l'
View
interface de, vous verrez que le type de retour debody
est un type associé:Cela signifie que vous spécifiez ce type en annotant la
body
propriété avec un type particulier de votre choix. La seule exigence est que ce type doit implémenter leView
protocole lui-même.Cela peut être soit un type spécifique qui implémente
View
, par exempleText
Image
Circle
ou un type opaque qui implémente
View
, c.-à-d.some View
Vues génériques
Le problème se pose lorsque nous essayons d'utiliser une vue de pile comme
body
type de retour de, commeVStack
ouHStack
:Cela ne se compilera pas et vous obtiendrez l'erreur:
C'est parce que les vues de pile dans SwiftUI sont des types génériques ! 💡 (Et il en va de même pour les listes et autres types de vue de conteneur.)
Cela a beaucoup de sens car vous pouvez brancher n'importe quel nombre de vues de tout type (tant qu'il est conforme au
View
protocole). Le type de bétonVStack
dans le corps ci-dessus est en faitLorsque nous décidons plus tard d'ajouter une vue à la pile, son type concret change. Si nous ajoutons un deuxième texte après le premier, nous obtenons
Même si nous apportons une modification mineure, quelque chose d'aussi subtil que l'ajout d'un espaceur entre le texte et l'image, le type de la pile change:
D'après ce que je peux dire, c'est la raison pour laquelle Apple recommande dans ses tutoriels de toujours utiliser
some View
, le type opaque le plus général que toutes les vues satisfont, comme lebody
type de retour du. Vous pouvez changer l'implémentation / la disposition de votre vue personnalisée sans changer manuellement le type de retour à chaque fois.Supplément:
Si vous souhaitez obtenir une compréhension plus intuitive des types de résultats opaques, j'ai récemment publié un article qui pourrait être intéressant à lire:
🔗 Qu'est-ce que «certains» dans SwiftUI?
la source
Je pense que toutes les réponses manquantes jusqu'à présent sont
some
utiles principalement dans quelque chose comme un DSL (langage spécifique au domaine) tel que SwiftUI ou une bibliothèque / framework, qui aura des utilisateurs (autres programmeurs) différents de vous.Vous n'utiliseriez probablement jamais
some
dans votre code d'application normal, sauf peut-être dans la mesure où il peut encapsuler un protocole générique afin qu'il puisse être utilisé comme type (au lieu de simplement comme contrainte de type). Qu'estsome
- ce que c'est de laisser le compilateur garder une connaissance de quel type spécifique quelque chose est, tout en mettant une façade de supertype devant lui.Ainsi, dans SwiftUI, où vous êtes l'utilisateur, tout ce que vous devez savoir, c'est que quelque chose est un
some View
, tandis que dans les coulisses, toutes sortes de mouchoirs peuvent continuer à vous protéger. Cet objet est en fait un type très spécifique, mais vous n'aurez jamais besoin de savoir de quoi il s'agit. Pourtant, contrairement à un protocole, c'est un type à part entière, car partout où il apparaît, ce n'est qu'une façade pour un type spécifique à part entière.Dans une future version de SwiftUI, où vous attendez un
some View
, les développeurs pourraient changer le type sous-jacent de cet objet particulier. Mais cela ne cassera pas votre code, car votre code n'a jamais mentionné le type sous-jacent en premier lieu.Ainsi,
some
en fait, un protocole ressemble davantage à une superclasse. C'est presque un type d'objet réel, mais pas tout à fait (par exemple, une déclaration de méthode d'un protocole ne peut pas retourner asome
).Donc, si vous deviez utiliser
some
quoi que ce soit, ce serait très probablement si vous écriviez un DSL ou un framework / bibliothèque à l'usage des autres, et que vous vouliez masquer les détails du type sous-jacent. Cela rendrait votre code plus simple à utiliser pour les autres et vous permettrait de modifier les détails d'implémentation sans casser leur code.Cependant, vous pouvez également l'utiliser dans votre propre code pour protéger une région de votre code des détails d'implémentation enfouis dans une autre région de votre code.
la source
Le
some
mot-clé de Swift 5.1 ( proposition d'évolution rapide ) est utilisé en conjonction avec un protocole comme type de retour.Les notes de version de Xcode 11 le présentent comme suit:
Dans l'exemple ci-dessus, vous n'avez pas besoin de dire que vous allez retourner un
Array
. Cela vous permet même de renvoyer un type générique qui est juste conformeCollection
.Notez également cette erreur possible que vous pourriez rencontrer:
Cela signifie que vous êtes censé utiliser la disponibilité pour éviter
some
sur iOS 12 et avant:la source
some
sur iOS 12 et avant. Tant que vous le faites, ça devrait aller. Le problème est seulement que le compilateur ne vous avertit pas de le faire.some
mot - clé dans cet exemple de code donné dans Swift 5.0 ou Swift 4.2. L'erreur sera: "Le protocole 'Collection' ne peut être utilisé que comme une contrainte générique car il a des exigences de type Self ou associées "«certains» signifie le type opaque. Dans SwiftUI, View est déclaré en tant que protocole
Lorsque vous créez votre vue en tant que Struct, vous vous conformez au protocole View et dites que le corps var renverra quelque chose qui confirmera à View Protocol. C'est comme une abstraction générique de protocole où vous n'avez pas à définir le type concret.
la source
Je vais essayer de répondre à cela avec un exemple pratique très basique (de quoi s'agit-il d' un type de résultat opaque )
En supposant que vous ayez un protocole avec le type associé et deux structures l'implémentant:
Avant Swift 5.1, ci-dessous est illégal en raison d'une
ProtocolWithAssociatedType can only be used as a generic constraint
erreur:Mais dans Swift 5.1 c'est très bien (
some
ajouté):Ci-dessus est une utilisation pratique, largement utilisée dans SwiftUI pour
some View
.Mais il y a une limitation importante - le type retourné doit être connu au moment de la compilation, donc ci-dessous ne fonctionnera pas à nouveau donnant une
Function declares an opaque return type, but the return statements in its body do not have matching underlying types
erreur:la source
Un cas d'utilisation simple qui me vient à l'esprit est l'écriture de fonctions génériques pour les types numériques.
la source
Pour ceux qui ont été étourdis par le sujet, voici un article très décryptant et pas à pas grâce à Vadim Bulavin.
https://www.vadimbulavin.com/opaque-return-types-and-the-some-keyword-in-swift/
la source