Johdatus Sillä Avainsana otettiin käyttöön Swift 5.1:ssä, kun Avainsana otettiin käyttöön Swift 5.6: ssä.Nyt näitä avainsanoja voidaan käyttää funktion parametreissa ja ennen tyypin määrittelyä seuraavasti: some any protocol P {} struct S: P {} // 'any P' is an explicit existential type. let p: any P = S() // 'some P' is an opaque type. let p: some P = S() func f(_ p: any P) {} func f(_ p: some P) {} Swift 5:ssä on Avainsana voidaan käyttää nimenomaisesti merkitsemään eksistentiaalinen tyyppi. Swift 6:stä alkaen eksistentiaalisten tyyppien on oltava nimenomaisesti kirjoitettu Avainsana on any any Epäselvä tyyppi auttaa kuvaamaan odotettua palautustyyppiä määrittelemättä tiettyä betonityyppiä.Tällä tavalla kompilaattori voi käyttää todellisia tyyppitietoja ja mahdollisesti suorittaa optimointeja tässä yhteydessä. Eksistentiaalinen tyyppi voi tallentaa mitä tahansa protokollan mukaista arvoa, ja arvotyyppi voi muuttua dynaamisesti, mikä edellyttää dynaamista muistin kohdentamista. Joidenkin avainsanojen ymmärtäminen Avainsana edustaa epäselvää tyyppiä. epäselvä tulostyyppi on implisiittinen yleinen paikanpitäjä, joka on täytäntöönpanon tyydyttämä, joten voit ajatella tätä: some protocol P {} struct S1 : P {} func f() -> some P { return S1() } Tärkein otos tässä on, että toiminto, joka tuottaa tyypin palauttaa yksittäisen, konkreettisen tyypin arvon, joka liittyy Jos toiminto yrittää palauttaa useita vastaavia tyyppejä, tuloksena on kompilaattorin virhe, koska implisiittistä yleistä sijaintia ei voida tyydyttää useilla tyyppeillä. P P struct F1: P {} struct F2: P {} // error: Function declares an opaque return type, but the return // statements in its body do not have matching underlying types. func f(_ value: Bool) -> some P { if value { return F1() } else { return F2() } } Harkitse etuja, joita epäselvät tyypit tarjoavat verrattuna protokollan palautustyyppeihin. Opaque result types can be used with PATs. The main limitation of using protocols is that protocols with associated types cannot be used as actual types. This means that the following code doesn’t compile: func collection() -> Collection { return ["1", "2", "3"] } As for opaque types, they are merely generic placeholders that can be used in such scenarios: // protocol Collection<Element> : Sequence func collection() -> some Collection { return ["1", "2", "3"] } Opaque result types have identity. Because opaque types guarantee that only one type will be returned, the compiler knows that a function must return the same type on several calls: func method() -> some Equatable { return "method" } let x = method() let y = method() print(x == y) // true Opaque result types compose with generic placeholders. Contrary to conventional protocol-typed values, opaque result types integrate effectively with standard generic placeholders. For instance: protocol P { var message: String { get } } struct M: P { var message: String } func makeM() -> some P { return M(message: "message") } func bar<T: P, U: P>(_ p1: T, _ p2: U) -> Bool { return p1.message == p2.message } let m1 = makeM() let m2 = makeM() print(bar(m1, m2)) However, it doesn’t work if make returns different types based on protocol . M() P protocol P { var message: String { get } } struct M: P { var message: String } struct T: P { var message: String } // error: function declares an opaque return type 'some P', but the return statements in its body do not have matching underlying types func makeM() -> some P { if .random() { return M(message: "M message") } else { return T(message: "T message") } } Jokaisen avainsanan ymmärtäminen Katsotaanpa seuraavaa esimerkkiä: Oletetaan, että meillä on pöytäkirja ja kaksi tämän pöytäkirjan konkreettista täytäntöönpanoa. Drawable protocol Drawable { func draw() } struct Line: Drawable { let x1: Int let y1: Int let x2: Int let y2: Int func draw() { print("Draw Line") } } struct Point: Drawable { let x: Int let y: Int func draw() { print("Point") } } Meillä on kaksi objektia, ja , vastaavasti. Luomme muuttujan ja tallennamme a kohteen mukaan: Line Point Drawable var p1: any Drawable = Line(x1: 0, y1: 0, x2: 5, y2: 5) // 'any Drawable' is an explicit existential type p1.draw() // print "Draw Line" p1 = Point(x: 0, y: 0) p1.draw() // print "Point" Voimme vaihtaa eri täytäntöönpanojen välillä käynnistyksen aikana. Harkitse toinen esimerkki: let array: [any Drawable] = [ Line(x1: 0, y1: 0, x2: 5, y2: 5), Line(x1: 0, y1: 0, x2: 5, y2: 5), Point(x: 0, y: 0) ] Kuten tiedämme, satunnainen pääsy mihin tahansa elementtiin sarjassa jatkuvassa ajassa on mahdollista, koska jokaisella elementillä on sama muistin koko.Meidän esimerkissämme, jossa tallennamme eri kokoisia elementtejä, saatat ihmetellä, miten voimme hakea elementtiä sarjasta tällaisessa skenaariossa. print(MemoryLayout<Line>.size) // 32 print(MemoryLayout<Point>.size) // 16 Se on mahdollista, koska Eksistentiaalinen säiliö sisällyttää viisi koneen sanaa, jaettuna kolme esineen tai osoittimen säilyttämiseksi esineeseen, yksi osoittimen virtuaalisen taulukon ja toinen osoittimen todistajapöytään. any Eksistentiaalinen säiliö ottaa 5 koneen sanaa (x64-bittisessä järjestelmässä, 5 * 8 = 40): Value buffer is a space for the instance VWT is a pointer to Value Witness Table PWT is a pointer to the Protocol Witness Table Kun protokollan arvoa käytetään koodissa, kompilaattori luo laatikon, jota kutsumme eksistentiaaliseksi säiliöksi. Yksi laatikko yhdelle arvolle. Koska ne voivat tallentaa minkä tahansa arvon, jonka tyyppi vastaa protokollaa, ja tallennetun arvon tyyppi voi muuttua dynaamisesti, eksistentiaalinen säiliö vaatii dynaamisen muistin kohdentamista, jos arvo ei ole riittävän pieni 3 koneen sanan puskurin sisälle. Kokoonpanon ja viitenumeron lisäksi jokainen eksistentiaalisen säiliön käyttö edellyttää osoittimen epäsuoraa ja dynaamista lähettämistä, jota ei voida optimoida. Kolme sanaa käytetään joko arvon liittämiseen, jos se sopii 3 koneen sanaan, tai jos arvo on enemmän kuin 3 koneen sanaa, luodaan ARC-hallittu laatikko. Arvo kopioidaan sitten laatikkoon ja viittaus ARC-hallittuun laatikkoon kopioidaan säiliön ensimmäiseen sanaan. Loput kaksi sanaa ei käytetä. Protocol Witness Table on sarja, joka sisältää yhden merkinnän jokaiselle protokollalle, joka on staattisesti yhdistetty eksistentiaaliseen tyyppiin. Niin kauan kuin meillä on Protocol Witness Table (PWT), joka kuvaa tyypin noudattamista protokollaan, voimme luoda eksistentiaalisen säiliön, ohittaa sen ja käyttää sitä dynaamiseen lähetykseen. P & Q Joten meillä on eksistentiaalinen säiliö, ja meillä on pöytäkirjan todistustaulukko. Emme kuitenkaan tiedä, missä tämä arvo olisi tallennettava eksistentiaaliseen säiliöön - onko se tallennettu suoraan säiliöön tai jos meillä on vain viittaus pinoon. ja ja ja Jokaisella tyypillä on tällainen taulukko, ja se ottaa vastuun tyypin esimerkin luomisesta, päättää, missä arvo tallennetaan - pinoon tai pinoon jne. allocate copy destruct deallocate Nyt meillä on kiinteä kokoinen säiliö, joka ratkaisee heterogeenisten kokoisten parametrien kulkeutumisen ongelman ja keinon siirtää tätä säiliötä. No, ainakin haluamme pystyä vedottamaan tarvittavat jäsenet tallennetun arvon säiliössä, kuten on määritetty protokollassa (initializers, ominaisuudet - onko tallennettu tai laskettu, toiminnot ja tilaukset). Kuitenkin kukin vastaava tyyppi voi toteuttaa nämä jäsenet eri tavalla. Esimerkiksi jotkut voivat tyydyttää menetelmävaatimuksen määrittelemällä sen suoraan, kun taas toinen, sano luokka, voi tyydyttää sen perimällä menetelmän superluokasta. Jotkut voivat toteuttaa omaisuuden tallennettuna omaisuutena, toiset laskennallisena ja jotkut takautuvasti (laajennuksen kautta). Kaiken tämän monimuotoisuuden käsitteleminen on pöytäkirjan todistajien taulukon (PWT) ensisijainen tarkoitus. Jokaisen pöytäkirjan vaatimustenmukaisuuden osalta on olemassa täsmälleen yksi tällainen taulukko. Vaikka toiminnot voivat sijaita eri paikoissa, PWT:n sijainti on ainutlaatuinen protokollalle, johon se vastaa. On tärkeää tietää, että eksistentiaalit ovat suhteellisen kalliita käyttää, koska kompilointi ja suoritusaika eivät voi ennalta määrittää, kuinka paljon muistia pitäisi kohdistaa konkreettiselle objektille, joka täyttää eksistentiaalinen. On tärkeää tietää, että eksistentiaalit ovat suhteellisen kalliita käyttää, koska kompilointi ja suoritusaika eivät voi ennalta määrittää, kuinka paljon muistia pitäisi kohdistaa konkreettiselle objektille, joka täyttää eksistentiaalinen. Yhteenveto Tarkastelemme eroja niiden välillä ja Toisaalta se paransi merkittävästi yleisen koodin syntaksiä ja luettavuutta; toisaalta se esitteli uusia tapoja luoda yleinen koodi tehokkaammin. some any Kiitos lukemisesta