Nowy samouczek SwiftUI zawiera następujący kod:
struct ContentView: View {
var body: some View {
Text("Hello World")
}
}
W drugim wierszu słowo some
i na ich stronie jest podświetlone, jakby było słowem kluczowym.
Swift 5.1 nie wydaje się być some
słowem kluczowym i nie widzę, co jeszcze słowo some
mogłoby tam robić, ponieważ idzie tam, gdzie zwykle idzie typ. Czy jest nowa, niezapowiedziana wersja Swift? Czy jest to funkcja używana w typie w sposób, o którym nie wiedziałam?
Co robi słowo kluczowe some
?
Odpowiedzi:
some View
jest nieprzezroczystym typem wyniku wprowadzonym przez SE-0244 i jest dostępny w Swift 5.1 z Xcode 11. Można to traktować jako ogólny symbol zastępczy „odwrotnej”.W przeciwieństwie do zwykłego ogólnego symbolu zastępczego, który jest wywoływany przez osobę dzwoniącą:
Nieprzezroczysty typ wyniku to domyślny ogólny symbol zastępczy spełniony przez implementację , więc możesz pomyśleć o tym:
wygląda tak:
W rzeczywistości ostatecznym celem tej funkcji jest zezwolenie na odwrotne generyczne w tej bardziej wyraźnej formie, co pozwoliłoby również na dodanie ograniczeń, np
-> <T : Collection> T where T.Element == Int
. Zobacz ten post, aby uzyskać więcej informacji .Najważniejszą rzeczą do usunięcia jest
some P
to, że zwracana funkcja to taka, która zwraca wartość określonego pojedynczego konkretnego typu, który jest zgodnyP
. Próba zwrócenia różnych zgodnych typów w ramach funkcji powoduje błąd kompilatora:Ponieważ domyślny ogólny symbol zastępczy nie może być spełniony przez wiele typów.
Jest to w przeciwieństwie do zwracanej funkcji
P
, która może być używana do reprezentowania obuS1
iS2
ponieważ reprezentuje dowolnąP
zgodną wartość:Okej, więc jakie korzyści mają nieprzejrzyste typy wyników w
-> some P
porównaniu do typów zwracanych przez protokół-> P
?1. Nieprzezroczyste typy wyników mogą być używane z PAT
Głównym bieżącym ograniczeniem protokołów jest to, że PAT (protokoły z powiązanymi typami) nie mogą być używane jako rzeczywiste typy. Chociaż jest to ograniczenie, które prawdopodobnie zostanie zniesione w przyszłej wersji języka, ponieważ nieprzezroczyste typy wyników są w rzeczywistości zwykłymi symbolami zastępczymi, można je dziś stosować z PAT.
Oznacza to, że możesz robić takie rzeczy jak:
2. Nieprzezroczyste typy wyników mają tożsamość
Ponieważ nieprzezroczyste typy wyników wymuszają zwrócenie jednego konkretnego typu, kompilator wie, że dwa wywołania tej samej funkcji muszą zwrócić dwie wartości tego samego typu.
Oznacza to, że możesz robić takie rzeczy jak:
Jest to legalne, ponieważ kompilator wie o tym
x
iy
ma ten sam konkretny typ. Jest to ważny wymóg==
, w przypadku gdy oba parametry typuSelf
.Oznacza to, że oczekuje dwóch wartości, które są tego samego typu, co typ zgodny z betonem. Nawet jeśli
Equatable
byłyby użyteczne jako typ, nie byłoby możliwe porównanie dwóch dowolnychEquatable
zgodnych wartości, na przykład:Ponieważ kompilator nie może udowodnić, że dwie dowolne
Equatable
wartości mają ten sam podstawowy typ betonu.W podobny sposób, jeśli wprowadzimy inną funkcję zwracania typu nieprzezroczystego:
Przykład staje się nielegalny, ponieważ mimo
foo
i obabar
zwracająsome Equatable
, ich „odwrócone” ogólne symbole zastępczeOutput1
iOutput2
mogą być zaspokojone przez różne typy.3. Nieprzezroczyste typy wyników składają się z ogólnych symboli zastępczych
W przeciwieństwie do zwykłych wartości typowych dla protokołu, nieprzezroczyste typy wyników dobrze komponują się ze zwykłymi rodzajowymi symbolami zastępczymi, na przykład:
To nie zadziałałoby, gdyby
makeP
właśnie wróciłoP
, ponieważ dwieP
wartości mogą mieć różne podstawowe typy betonu, na przykład:Dlaczego warto stosować nieprzejrzysty typ wyniku zamiast rodzaju betonu?
W tym momencie możesz pomyśleć, dlaczego nie napisać kodu jako:
Cóż, użycie nieprzezroczystego typu wyniku pozwala uczynić ten typ
S
szczegółem implementacji, odsłaniając tylko interfejs zapewniony przezP
, co daje elastyczność zmiany konkretnego typu później w dół linii bez łamania kodu zależnego od funkcji.Na przykład możesz zastąpić:
z:
bez łamania wywoływanego kodu
makeP()
.Więcej informacji na temat tej funkcji znajduje się w sekcji Typy nieprzezroczyste przewodnika językowego i propozycji Szybkiej ewolucji .
źródło
return
nie jest wymagany w funkcjach z jednym wyrażeniemfunc makeP() -> some P
afunc makeP() -> P
? Przeczytałem propozycję i nie widzę tej różnicy także dla ich próbek.some P
byłoby to potrzebneDruga odpowiedź dobrze wyjaśnia techniczny aspekt nowego
some
słowa kluczowego, ale ta odpowiedź będzie próbowała łatwo wyjaśnić, dlaczego .Powiedzmy, że mam protokół Animal i chcę porównać, czy dwa zwierzęta są rodzeństwem:
W ten sposób sensowne jest porównanie, czy dwa zwierzęta są rodzeństwem, jeśli są tego samego rodzaju .
Teraz pozwól mi po prostu stworzyć przykład zwierzęcia tylko dla odniesienia
Droga bez
some T
Powiedzmy teraz, że mam funkcję, która zwraca zwierzę z „rodziny”.
Teraz pojawia się problem, jeśli spróbuję to zrobić:
Spowoduje to błąd .
Czemu? Powodem jest to, że kiedy dzwonisz do
animal1.isSibling(animal2)
Swift, nie wie, czy zwierzęta to psy, koty, czy cokolwiek innego. O ile Swift wieanimal1
ianimal2
mogą to być niepowiązane gatunki zwierząt . Ponieważ nie możemy porównywać zwierząt różnych typów (patrz wyżej). To spowoduje błądJak
some T
rozwiązuje ten problemPrzepiszmy poprzednią funkcję:
animal1
i nieanimal2
są , ale są klasą, która implementuje Animal .Animal
Teraz możesz to zrobić, kiedy dzwonisz
animal1.isSibling(animal2)
, Swift wie o tymanimal1
ianimal2
jest tego samego typu.Tak więc lubię o tym myśleć:
(Wyłączenie odpowiedzialności za autopromocję) Napisałem post na blogu, który bardziej szczegółowo przedstawia tę nową funkcję
źródło
some
w zamian, typ działa jako ograniczenie treści funkcji.some
Wymaga więc zwrócenia tylko jednego konkretnego typu w całym ciele funkcji. Na przykład: jeśli tak,return randomDog
wszystkie inne zwroty muszą działać tylko zDog
. Wszystkie korzyści wynikają z tego ograniczenia: dostępnośćanimal1.isSibling(animal2)
i korzyść z kompilacjifunc animalFromAnimalFamily() -> some Animal
(ponieważ terazSelf
zostanie zdefiniowana pod maską). Czy to jest poprawne?Odpowiedź Hamisha jest niesamowita i odpowiada na pytanie z technicznego punktu widzenia. Chciałbym dodać kilka przemyśleń na temat tego, dlaczego słowo kluczowe
some
jest używane w tym konkretnym miejscu w samouczkach Apple SwiftUI i dlaczego warto go przestrzegać.some
nie jest wymaganiem!Przede wszystkim nie musisz deklarować
body
typu zwracanego jako nieprzejrzystego. Zawsze możesz zwrócić konkretny typ zamiast używaćsome View
.To również się skompiluje. Gdy spojrzysz na
View
interfejs, zobaczysz, że zwracanym typembody
jest typ powiązany:Oznacza to, że określasz ten typ, dodając adnotację do
body
właściwości wybranego rodzaju. Jedynym wymaganiem jest to, że ten typ musi implementowaćView
sam protokół.Które mogą być albo specyficzny typ, który implementuje
View
na przykładText
Image
Circle
lub nieprzezroczysty typ, który implementuje
View
, tjsome View
Ogólne widoki
Problem pojawia się, gdy staramy się korzystać z widoku stosu jako
body
„s typ zwracany, jakVStack
iHStack
:To się nie skompiluje i pojawi się błąd:
To dlatego, że widoki stosu w SwiftUI są rodzajami rodzajowymi ! 💡 (To samo dotyczy list i innych typów widoków kontenerów).
Ma to sens, ponieważ można podłączyć dowolną liczbę widoków dowolnego typu (o ile jest to zgodne z
View
protokołem). Konkretny typVStack
powyższego ciała jest w rzeczywistościKiedy później decydujemy się dodać widok do stosu, zmienia się jego konkretny typ. Jeśli dodamy drugi tekst po pierwszym, otrzymamy
Nawet jeśli dokonamy drobnej zmiany, czegoś tak subtelnego jak dodanie odstępu między tekstem a obrazem, zmieni się rodzaj stosu:
Z tego, co mogę powiedzieć, to jest powód, dla którego Apple zaleca w swoich tutorialach, aby zawsze używać
some View
najbardziej ogólnego nieprzezroczystego typu, który spełniają wszystkie widoki, jakobody
typu zwracanego. Możesz zmienić implementację / układ niestandardowego widoku bez ręcznej zmiany typu zwrotu za każdym razem.Suplement:
Jeśli chcesz bardziej intuicyjnie zrozumieć nieprzejrzyste typy wyników, niedawno opublikowałem artykuł, który warto przeczytać:
🔗 Co to jest „trochę” w SwiftUI?
źródło
Myślę, że do tej pory brakuje wszystkich odpowiedzi, które
some
są przydatne przede wszystkim w czegoś takiego jak DSL (język specyficzny dla domeny), taki jak SwiftUI lub biblioteka / framework, w którym użytkownicy (inni programiści) będą inni niż ty.Prawdopodobnie nigdy nie użyłbyś
some
normalnego kodu aplikacji, z wyjątkiem być może o tyle, o ile może on zawrzeć ogólny protokół, aby można go było używać jako typu (zamiast ograniczenia typu). Cosome
robi to niech kompilator zachować wiedzę o tym, co specyficzny rodzaj coś jest, a oddanie supertypem elewację przed nim.Zatem w SwiftUI, gdzie jesteś użytkownikiem, wszystko, co musisz wiedzieć, to że coś jest
some View
, podczas gdy za kulisami może się dziać wszelkiego rodzaju chanke-panky, przed którymi jesteś osłonięty. Ten obiekt jest w rzeczywistości bardzo specyficznym typem, ale nigdy nie musisz słyszeć o tym, co to jest. Jednak w przeciwieństwie do protokołu, jest to pełnoprawny typ, ponieważ gdziekolwiek się pojawia, jest jedynie fasadą dla jakiegoś konkretnego pełnoprawnego typu.W przyszłej wersji SwiftUI, w której oczekujesz a
some View
, programiści mogą zmienić podstawowy typ tego konkretnego obiektu. Ale to nie zepsuje twojego kodu, ponieważ twój kod nigdy nie wspominał o typie podstawowym.W
some
efekcie protokół bardziej przypomina nadklasę. Jest to prawie prawdziwy typ obiektu, choć niezupełnie (na przykład deklaracja metody protokołu nie może zwrócić asome
).Więc jeśli zamierzasz używać
some
do czegokolwiek, najprawdopodobniej byłoby to, gdybyś pisał DSL lub framework / bibliotekę do użytku przez innych i chciałbyś zamaskować podstawowe szczegóły typu. Ułatwi to korzystanie z kodu innym użytkownikom i pozwoli na zmianę szczegółów implementacji bez łamania ich kodu.Możesz jednak również użyć go we własnym kodzie jako sposobu ochrony jednego regionu kodu przed szczegółami implementacji zakopanymi w innym regionie kodu.
źródło
Słowo
some
kluczowe z Swift 5.1 ( propozycja swift-evolution ) jest używane w połączeniu z protokołem jako typ zwracany.Xcode 11 Uwagi do wydania przedstawić to tak:
W powyższym przykładzie nie musisz mówić, że zwrócisz
Array
. To pozwala nawet zwrócić ogólny typ, który jest po prostu zgodnyCollection
.Zwróć również uwagę na ten możliwy błąd, który możesz napotkać:
Oznacza to, że należy korzystać z dostępności, aby uniknąć
some
na iOS 12 i wcześniej:źródło
some
na iOS 12 i wcześniejszych. Tak długo, jak to robisz, powinno być w porządku. Problem polega tylko na tym, że kompilator nie ostrzega cię przed tym.some
słowa kluczowego w podanym przykładzie kodu w Swift 5.0 lub Swift 4.2. Błąd będzie: „ protokołem«zbieranie»może być używany tylko jako ogólne ograniczenia, ponieważ ma Jaźni lub związane wymagania typu ”„niektóre” oznaczają rodzaj nieprzezroczysty. W SwiftUI View jest deklarowany jako protokół
Kiedy tworzysz swój widok jako Struct, zgadzasz się z protokołem View i mówisz, że ciało var zwróci coś, co potwierdzi protokół View. To jak ogólna abstrakcja protokołu, w której nie trzeba definiować konkretnego typu.
źródło
Spróbuję odpowiedzieć na to bardzo podstawowym praktycznym przykładem (o czym jest ten nieprzejrzysty typ wyniku )
Zakładając, że masz protokół z powiązanym typem i dwie struktury go implementujące:
Przed wersją Swift 5.1 poniżej jest nielegalne z powodu
ProtocolWithAssociatedType can only be used as a generic constraint
błędu:Ale w Swift 5.1 jest w porządku (
some
dodano):Powyżej znajduje się praktyczne zastosowanie, szeroko stosowane w SwiftUI dla
some View
.Ale jest jedno ważne ograniczenie - zwracany typ musi być znany w czasie kompilacji, więc poniżej znowu nie będzie działać, dając
Function declares an opaque return type, but the return statements in its body do not have matching underlying types
błąd:źródło
Prostym przypadkiem, który przychodzi mi do głowy, jest pisanie ogólnych funkcji dla typów numerycznych.
źródło
Dla tych, którzy byli oszołomieni tematem, tutaj bardzo odszyfrowujący i krok po kroku artykuł dzięki Vadimowi Bulavinowi.
https://www.vadimbulavin.com/opaque-return-types-and-the-some-keyword-in-swift/
źródło