Das neue SwiftUI-Tutorial enthält den folgenden Code:
struct ContentView: View {
var body: some View {
Text("Hello World")
}
}
In der zweiten Zeile wird das Wort some
und auf ihrer Website hervorgehoben, als wäre es ein Schlüsselwort.
Swift 5.1 scheint kein some
Schlüsselwort zu haben , und ich sehe nicht, was das Wort dort some
sonst noch tun könnte, da es dahin geht, wo der Typ normalerweise hingeht. Gibt es eine neue, unangekündigte Version von Swift? Ist es eine Funktion, die für einen Typ auf eine Weise verwendet wird, von der ich nichts wusste?
Was macht das Schlüsselwort some
?
Antworten:
some View
ist ein undurchsichtiger Ergebnistyp, wie er von SE-0244 eingeführt wurde, und ist in Swift 5.1 mit Xcode 11 verfügbar. Sie können sich dies als einen "umgekehrten" generischen Platzhalter vorstellen.Im Gegensatz zu einem normalen generischen Platzhalter, der vom Anrufer erfüllt wird:
Ein undurchsichtiger Ergebnistyp ist ein impliziter generischer Platzhalter, der von der Implementierung erfüllt wird. Sie können sich also Folgendes vorstellen:
so aussehend:
Tatsächlich besteht das letztendliche Ziel dieser Funktion darin, umgekehrte Generika in dieser expliziteren Form zuzulassen, wodurch Sie auch Einschränkungen hinzufügen können, z
-> <T : Collection> T where T.Element == Int
. Weitere Informationen finden Sie in diesem Beitrag .Die Hauptsache wegzunehmen ist , dass eine Funktion der Rückkehr
some P
ist ein , dass die Renditen ein Wert eines bestimmten Einzel konkreter dass KonformP
. Der Versuch, verschiedene konforme Typen innerhalb der Funktion zurückzugeben, führt zu einem Compilerfehler:Da der implizite generische Platzhalter nicht von mehreren Typen erfüllt werden kann.
Dies steht im Gegensatz zu einer zurückgegebenen Funktion
P
, die verwendet werden kann, um beide darzustellen,S1
undS2
weil sie einen beliebigenP
konformen Wert darstellt:Okay, welche Vorteile haben undurchsichtige Ergebnistypen
-> some P
gegenüber Protokollrückgabetypen-> P
?1. Undurchsichtige Ergebnistypen können mit PATs verwendet werden
Eine wesentliche derzeitige Einschränkung von Protokollen besteht darin, dass PATs (Protokolle mit zugehörigen Typen) nicht als tatsächliche Typen verwendet werden können. Obwohl dies eine Einschränkung ist, die wahrscheinlich in einer zukünftigen Version der Sprache aufgehoben wird, da undurchsichtige Ergebnistypen praktisch nur generische Platzhalter sind, können sie heute mit PATs verwendet werden.
Dies bedeutet, dass Sie Dinge tun können wie:
2. Undurchsichtige Ergebnistypen haben Identität
Da undurchsichtige Ergebnistypen erzwingen, dass ein einzelner konkreter Typ zurückgegeben wird, weiß der Compiler, dass zwei Aufrufe derselben Funktion zwei Werte desselben Typs zurückgeben müssen.
Dies bedeutet, dass Sie Dinge tun können wie:
Das ist legal , da der Compiler weiß , dass beide
x
undy
den gleichen Betontyp haben. Dies ist eine wichtige Voraussetzung für==
beide Parameter des TypsSelf
.Dies bedeutet, dass zwei Werte erwartet werden, die beide vom gleichen Typ sind wie der konkrete konforme Typ. Selbst wenn Sie
Equatable
als Typ verwendet werden könnten, könnten Sie nicht zwei beliebigeEquatable
übereinstimmende Werte miteinander vergleichen , zum Beispiel:Da der Compiler nicht beweisen kann, dass zwei beliebige
Equatable
Werte den gleichen zugrunde liegenden konkreten Typ haben.In ähnlicher Weise, wenn wir eine andere undurchsichtige Rückgabefunktion vom Typ eingeführt haben:
Das Beispiel wird illegal, weil obwohl beide
foo
undbar
zurückkehrensome Equatable
, ihre "umgekehrten" generischen PlatzhalterOutput1
undOutput2
von verschiedenen Typen erfüllt werden könnten.3. Undurchsichtige Ergebnistypen bestehen aus generischen Platzhaltern
Im Gegensatz zu regulären protokolltypisierten Werten lassen sich undurchsichtige Ergebnistypen gut mit regulären generischen Platzhaltern kombinieren, zum Beispiel:
Dies hätte nicht funktioniert, wenn
makeP
es gerade zurückgegeben worden wäreP
, da zweiP
Werte unterschiedliche zugrunde liegende konkrete Typen haben können, zum Beispiel:Warum einen undurchsichtigen Ergebnistyp über dem Betontyp verwenden?
An diesem Punkt denken Sie sich vielleicht, warum schreiben Sie den Code nicht einfach wie folgt:
Durch die Verwendung eines undurchsichtigen Ergebnistyps können Sie den Typ zu
S
einem Implementierungsdetail machenP
, indem Sie nur die von bereitgestellte Schnittstelle verfügbar machen. So können Sie den konkreten Typ später flexibel ändern, ohne den von der Funktion abhängigen Code zu beschädigen.Zum Beispiel könnten Sie ersetzen:
mit:
ohne einen Code zu brechen, der aufruft
makeP()
.Siehe den Opaque Arten Abschnitt der Sprachführung und die Swift Evolution Vorschlag für weitere Informationen zu dieser Funktion.
quelle
return
ist dies für Funktionen mit einfachem Ausdruck nicht erforderlichfunc makeP() -> some P
undfunc makeP() -> P
? Ich habe den Vorschlag gelesen und kann diesen Unterschied auch für ihre Proben nicht erkennen.some P
benötigt würdeDie andere Antwort erklärt den technischen Aspekt des neuen
some
Schlüsselworts gut, aber diese Antwort versucht leicht zu erklären, warum .Angenommen, ich habe ein Protokolltier und möchte vergleichen, ob zwei Tiere Geschwister sind:
Auf diese Weise ist es nur sinnvoll zu vergleichen, ob zwei Tiere Geschwister sind, wenn sie dieselbe Art von Tier sind.
Lassen Sie mich nun nur ein Beispiel für ein Tier als Referenz erstellen
Der Weg ohne
some T
Nehmen wir jetzt an, ich habe eine Funktion, die ein Tier aus einer 'Familie' zurückgibt.
Jetzt kommt das Problem, wenn ich das versuche:
Dies wird einen Fehler auslösen .
Warum? Der Grund ist, wenn Sie
animal1.isSibling(animal2)
Swift anrufen, wissen Sie nicht, ob die Tiere Hunde, Katzen oder was auch immer sind. Soweit Swift kennt,animal1
undanimal2
nicht verwandte Tierarten sein könnte . Da können wir keine Tiere verschiedener Arten vergleichen (siehe oben). Dies wird ein Fehler seinWie
some T
löst dieses Problem?Schreiben wir die vorherige Funktion neu:
animal1
undanimal2
sind es nichtAnimal
, aber sie sind eine Klasse, die Animal implementiert .Damit können Sie jetzt anrufen
animal1.isSibling(animal2)
, Swift weiß dasanimal1
undanimal2
ist vom selben Typ.So wie ich darüber nachdenke:
(Haftungsausschluss für Eigenwerbung) Ich habe einen Blog-Beitrag geschrieben , der etwas ausführlicher (dasselbe Beispiel wie hier) zu dieser neuen Funktion ist
quelle
some
in der Rückgabe verstanden habe, funktioniert der Typ als Einschränkung des Funktionskörpers. Es muss alsosome
nur ein konkreter Typ im gesamten Funktionskörper zurückgegeben werden. Beispiel: Wenn dies der Fall ist, müssenreturn randomDog
alle anderen Rückgaben nur mit funktionierenDog
. Alle Vorteile ergeben sich aus dieser Einschränkung: Verfügbarkeitanimal1.isSibling(animal2)
und Nutzen der Zusammenstellung vonfunc animalFromAnimalFamily() -> some Animal
(weil jetztSelf
unter der Haube definiert wird). Ist es richtig?Hamishs Antwort ist ziemlich beeindruckend und beantwortet die Frage aus technischer Sicht. Ich möchte einige Gedanken dazu hinzufügen, warum das Schlüsselwort
some
an dieser Stelle in den SwiftUI-Tutorials von Apple verwendet wird und warum es eine gute Vorgehensweise ist, dies zu befolgen.some
ist keine Voraussetzung!Zunächst einmal müssen Sie nicht brauchen , die zu erklären
body
‚s Rückgabetyp als opaken Typen. Sie können den konkreten Typ jederzeit zurückgeben, anstatt den zu verwendensome View
.Dies wird ebenfalls kompiliert. Wenn Sie sich die
View
Benutzeroberfläche ansehen , werden Sie feststellen, dass der Rückgabetypbody
ein zugeordneter Typ ist:Dies bedeutet, dass Sie diesen Typ angeben, indem Sie die
body
Eigenschaft mit einem bestimmten Typ Ihrer Wahl versehen. Die einzige Voraussetzung ist, dass dieser Typ dasView
Protokoll selbst implementieren muss.Dies kann entweder ein bestimmter Typ sein, der beispielsweise implementiert
View
wirdText
Image
Circle
oder ein undurchsichtiger Typ, der implementiert
View
, dhsome View
Generische Ansichten
Das Problem tritt auf, wenn wir versuchen, eine Stapelansicht als
body
Rückgabetyp zu verwenden, wieVStack
oderHStack
:Dies wird nicht kompiliert und Sie erhalten den Fehler:
Das ist , weil Stapel Ansichten in SwiftUI sind generische Typen! 💡 (Gleiches gilt für Listen und andere Containeransichtstypen.)
Das ist sehr sinnvoll, da Sie eine beliebige Anzahl von Ansichten eines beliebigen Typs anschließen können (sofern dies dem
View
Protokoll entspricht). Der konkrete Typ desVStack
im Körper oben ist tatsächlichWenn wir später beschließen, dem Stapel eine Ansicht hinzuzufügen, ändert sich sein konkreter Typ. Wenn wir nach dem ersten einen zweiten Text hinzufügen, erhalten wir
Selbst wenn wir eine geringfügige Änderung vornehmen, die so subtil ist wie das Hinzufügen eines Abstandshalters zwischen Text und Bild, ändert sich der Typ des Stapels:
Von dem, was ich sagen kann, das ist , der Grund , warum Apple empfiehlt in ihren Tutorials immer zu verwenden
some View
, die allgemeinste undurchsichtige Art , die alle Ansichten zu erfüllen, alsbody
Rückgabetyp s‘. Sie können die Implementierung / das Layout Ihrer benutzerdefinierten Ansicht ändern, ohne den Rückgabetyp jedes Mal manuell zu ändern.Ergänzung:
Wenn Sie ein intuitiveres Verständnis der undurchsichtigen Ergebnistypen erhalten möchten, habe ich kürzlich einen Artikel veröffentlicht, der möglicherweise lesenswert ist:
🔗 Was ist das für ein "in SwiftUI"?
quelle
Ich denke, was bisher alle Antworten fehlen, ist, dass
some
es vor allem in so etwas wie einer DSL (domänenspezifischen Sprache) wie SwiftUI oder einer Bibliothek / einem Framework nützlich ist, bei der Benutzer (andere Programmierer) anders sind als Sie.Sie würden wahrscheinlich nie
some
in Ihrem normalen App-Code verwenden, außer vielleicht insofern, als es ein generisches Protokoll umschließen kann, damit es als Typ verwendet werden kann (anstatt nur als Typeinschränkung). Wassome
tut , ist der Compiler hält ein Wissen von dem, was etwas bestimmten Art zu lassen, während einer übergeordneten Typ Fassade davor setzen.In SwiftUI, wo Sie der Benutzer sind, müssen Sie also nur wissen, dass etwas ein ist
some View
, während hinter den Kulissen alle Arten von Taschentüchern weitergehen können, von denen Sie abgeschirmt sind. Dieses Objekt ist in der Tat ein sehr spezifischer Typ, aber Sie müssen nie erfahren, was es ist. Im Gegensatz zu einem Protokoll handelt es sich jedoch um einen vollwertigen Typ, da es, wo immer es erscheint, lediglich eine Fassade für einen bestimmten vollwertigen Typ ist.In einer zukünftigen Version von SwiftUI, in der Sie a erwarten
some View
, können die Entwickler den zugrunde liegenden Typ dieses bestimmten Objekts ändern. Aber das wird Ihren Code nicht beschädigen, da Ihr Code den zugrunde liegenden Typ überhaupt nicht erwähnt hat.Somit
some
macht in der Tat ein Protokoll eher wie ein Super. Es ist fast ein realer Objekttyp, wenn auch nicht ganz (zum Beispiel kann die Methodendeklaration eines Protokolls kein a zurückgebensome
).Wenn Sie also
some
für irgendetwas verwenden würden, wäre es höchstwahrscheinlich, wenn Sie ein DSL oder ein Framework / eine Bibliothek zur Verwendung durch andere schreiben würden und die zugrunde liegenden Typdetails maskieren wollten. Dies würde die Verwendung Ihres Codes für andere einfacher machen und es Ihnen ermöglichen, die Implementierungsdetails zu ändern, ohne deren Code zu beschädigen.Sie können es jedoch auch in Ihrem eigenen Code verwenden, um eine Region Ihres Codes vor den Implementierungsdetails zu schützen, die in einer anderen Region Ihres Codes vergraben sind.
quelle
Das
some
Schlüsselwort aus Swift 5.1 ( Swift-Evolution-Vorschlag ) wird in Verbindung mit einem Protokoll als Rückgabetyp verwendet.Xcode 11 Release Notes präsentieren es so:
Im obigen Beispiel müssen Sie nicht angeben, dass Sie eine zurückgeben werden
Array
. Auf diese Weise können Sie sogar einen generischen Typ zurückgeben, der nur dem entsprichtCollection
.Beachten Sie auch diesen möglichen Fehler, mit dem Sie möglicherweise konfrontiert werden:
Dies bedeutet, dass Sie die Verfügbarkeit verwenden sollten, um dies
some
unter iOS 12 und früher zu vermeiden :quelle
some
unter iOS 12 und früher zu vermeiden . Solange Sie dies tun, sollte es Ihnen gut gehen. Das Problem ist nur, dass der Compiler Sie nicht davor warnt.some
Schlüsselwort in diesem angegebenen Codebeispiel in Swift 5.0 oder Swift 4.2 zu entfernen . Fehler wird sein: " Protokoll 'Sammlung' kann nur als generische Einschränkung verwendet werden, da es Selbst- oder zugehörige Typanforderungen hat "'einige' bedeutet undurchsichtigen Typ. In SwiftUI wird View als Protokoll deklariert
Wenn Sie Ihre Ansicht als Struktur erstellen, entsprechen Sie dem Ansichtsprotokoll und teilen mit, dass der var body etwas zurückgibt, das das Ansichtsprotokoll bestätigt. Es ist wie eine generische Protokollabstraktion, bei der Sie den konkreten Typ nicht definieren müssen.
quelle
Ich werde versuchen , dies mit sehr einfachen Beispiel aus der Praxis zu beantworten (was das ist ein undurchsichtiges Ergebnistyp über)
Angenommen, Sie haben ein Protokoll mit zugeordnetem Typ und zwei Strukturen, die es implementieren:
Vor Swift 5.1 ist Folgendes aufgrund eines
ProtocolWithAssociatedType can only be used as a generic constraint
Fehlers unzulässig :Aber in Swift 5.1 ist das in Ordnung (
some
hinzugefügt):Oben ist die praktische Verwendung aufgeführt, die in SwiftUI häufig für verwendet wird
some View
.Es gibt jedoch eine wichtige Einschränkung: Der zurückgegebene Typ muss zur Kompilierungszeit bekannt sein, sodass die folgenden Schritte nicht funktionieren und
Function declares an opaque return type, but the return statements in its body do not have matching underlying types
Fehler verursachen:quelle
Ein einfacher Anwendungsfall, der mir in den Sinn kommt, ist das Schreiben allgemeiner Funktionen für numerische Typen.
quelle
Für diejenigen, denen das Thema schwindelig war, hier ein sehr entschlüsselnder und schrittweiser Artikel dank Vadim Bulavin.
https://www.vadimbulavin.com/opaque-return-types-and-the-some-keyword-in-swift/
quelle