Swift Academy · Oppitunti

UnsafePointer ja UnsafeMutablePointer

Lue ja kirjoita raakaa muistia huolellisesti.

Oppitunti 2/413 vaihetta

UnsafePointer ja UnsafeMutablePointer on ilmainen Swift Academy-oppitunti CoddyKitissä. Tämä on oppitunti 2/4. Voit lukea tästä oppimispolusta kokonaan mitkä tahansa 3 oppituntia ilmaiseksi — sen jälkeen CoddyKit PRO avaa kaikki oppitunnit sekä käytännön harjoittelun sisäänrakennetulla koodieditorilla ja ympäri vuorokauden toimivalla tekoälytuutorilla. Oppitunti kuuluu Swift Academy-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. Swift Academy-kurssilla on yhteensä 4 oppituntia.

Tyypitetyt osoittimet Swiftissä

Swiftin turvalliset viitteet eivät paljasta raakaosoitteita, mutta kun yhteensopivuutta tarvitaan C:n kanssa tai rakennetaan matalan tason tietorakenteita, käytetään tyypitettyjä osoittimia: UnsafePointer<T> vain lukuun ja UnsafeMutablePointer<T> lukuun ja kirjoittamiseen.

Tyypitetty osoitin tuntee elementin tyypin, joten se voi laskea siirtymät ja lukea oikein tyypitetyn arvon.

// A pointer to an Int we can read but not write
func readFirst(_ p: UnsafePointer<Int>) -> Int {
    return p.pointee
}

pointee-ominaisuus

pointee on arvo, johon osoitin tällä hetkellä viittaa. Muuttuvalla osoittimella voit sekä lukea että määrittää sen arvon; vain lukuun tarkoitetulla osoittimella voit ainoastaan lukea.

pointee-ominaisuuden käyttäminen alustamattomassa tai vapautetussa muistissa aiheuttaa määrittelemätöntä toimintaa — tyyppijärjestelmä ei havaitse tätä virhettä.

let p = UnsafeMutablePointer<Int>.allocate(capacity: 1)
p.initialize(to: 42)
print(p.pointee)   // 42
p.pointee = 99
print(p.pointee)   // 99

Muistin varaaminen

UnsafeMutablePointer<T>.allocate(capacity:) varaa tilaa capacity määrälle elementtejä, mutta ei alusta niitä. Muisti on raakaa, ja sen lukeminen ennen alustusta on kiellettyä.

// Reserve space for 3 Doubles (uninitialized)
let buffer = UnsafeMutablePointer<Double>.allocate(capacity: 3)
// Must initialize before reading any element

Alustaminen ja alustuksen purkaminen

Ennen lukemista sinun on kutsuttava initialize-metodia. Ennen puhdistusta tarvitsevien tyyppien, kuten luokkien tai merkkijonojen, vapauttamista sinun on kutsuttava deinitialize-metodia niiden resurssien vapauttamiseksi.

Elinkaari on: allocate → initialize → use → deinitialize → deallocate.

let p = UnsafeMutablePointer<String>.allocate(capacity: 1)
p.initialize(to: "hello")
print(p.pointee)
p.deinitialize(count: 1) // releases the String
p.deallocate()

Yhdistä allocate aina deallocateen

Jokaista allocate-kutsua kohden on tehtävä täsmälleen yksi deallocate-kutsu, muuten muisti vuotaa. defer-lauseen käyttäminen heti varauksen jälkeen on turvallisin toimintamalli, koska se suoritetaan riippumatta siitä, miten funktio päättyy.

func process() {
    let p = UnsafeMutablePointer<Int>.allocate(capacity: 1)
    defer { p.deallocate() }
    p.initialize(to: 7)
    print(p.pointee)
} // deallocate runs here automatically

Osoitinlaskenta

Tyypitetyt osoittimet tukevat indeksointia ja laskentaa elementtiyksiköissä, eivät tavuissa. p + 2 siirtää osoitinta kahden T-tyypin siirtymän verran. p[i] on lyhenne ilmaisulle (p + i).pointee.

let p = UnsafeMutablePointer<Int>.allocate(capacity: 3)
p.initialize(repeating: 0, count: 3)
p[0] = 10
p[1] = 20
p[2] = 30
print((p + 1).pointee) // 20
p.deinitialize(count: 3)
p.deallocate()

Useiden elementtien alustaminen

Muistilohkossa initialize(repeating:count:) asettaa jokaisen paikan samaan arvoon, kun taas initialize(from:count:) kopioi arvot toisesta puskurista. Molemmat alustavat koko alueen, joten sitä on turvallista lukea.

let dst = UnsafeMutablePointer<Int>.allocate(capacity: 4)
dst.initialize(repeating: 5, count: 4)
print(dst[3]) // 5
dst.deinitialize(count: 4)
dst.deallocate()

Vain lukuun tarkoitettu ja muuttuva

Dataa vain tarkastelevan funktion tulee ottaa parametriksi UnsafePointer<T>; dataa muuttavan funktion tulee ottaa UnsafeMutablePointer<T>. Muuttuva osoitin muunnetaan implisiittisesti vain lukuun tarkoitetuksi osoittimeksi, mutta päinvastainen muunnos ei onnistu — näin rajapinta ilmaisee käyttötarkoituksen.

func sum(_ p: UnsafePointer<Int>, _ n: Int) -> Int {
    var total = 0
    for i in 0..<n { total += p[i] }
    return total
}
// A mutable pointer can be passed here directly

withUnsafePointer paikallisille arvoille

Jos haluat olemassa olevaan arvoon kohdistuvan väliaikaisen osoittimen ilman varaamista, käytä withUnsafePointer(to:)-metodia tai sen muuttuvaa versiota. Osoitin on kelvollinen vain sulkeuman sisällä — älä koskaan päästä sitä sulkeuman ulkopuolelle.

var value = 100
withUnsafeMutablePointer(to: &value) { ptr in
    ptr.pointee += 1
}
print(value) // 101

Yleiset sudenkuopat

Tyypitetyt osoittimet ovat tehokkaita, mutta vaativat tarkkuutta:

  • pointee-ominaisuuden lukeminen ennen initialize-kutsua aiheuttaa määrittelemätöntä toimintaa.
  • deinitialize-kutsun unohtaminen resursseja sisältäviltä tyypeiltä aiheuttaa vuotoja.
  • withUnsafePointer-osoittimen päästäminen sulkeuman ulkopuolelle synnyttää roikkuvan osoittimen.

Kääntäjä luottaa tässä sinuun — ajonaikaisia suojakaiteita ei ole.

// DANGEROUS — pointer escapes the closure
// var escaped: UnsafePointer<Int>?
// withUnsafePointer(to: &x) { escaped = $0 }
// escaped!.pointee  // dangling, undefined behavior

Pieni käsin toteutettu pino

Kun yhdistät kaiken opitun: varaa lohko, käsittele sitä elementteinä indeksoinnin avulla ja siivoa lopuksi resurssit. Tämä on mukautettujen kokoelmien ja C-yhteensopivuuden perusta.

let n = 3
let stack = UnsafeMutablePointer<Int>.allocate(capacity: n)
stack.initialize(repeating: 0, count: n)
for i in 0..<n { stack[i] = i * i }
// stack now holds 0, 1, 4
stack.deinitialize(count: n)
stack.deallocate()

Pikatarkistus

Palauta mieleen oikea osoittimen elinkaari.

Kertaus

Nyt tunnet tyypitetyt osoittimet:

  • UnsafePointer lukee, UnsafeMutablePointer lukee ja kirjoittaa.
  • pointee käsittelee arvoa; laskenta tapahtuu elementtiyksiköissä.
  • Elinkaari on allocate → initialize → use → deinitialize → deallocate, ja se kannattaa suojata defer-lauseella.

Seuraavaksi näet, miten buffer-osoittimet käärivät kokonaisen yhtenäisen alueen Collection-ystävälliseksi rajapinnaksi.

Aloita maksutta

Opi Swift tekoälytuutorin avulla — ilmaiseksi

Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.

Kurssit
122
Oppitunnit
409

Usein kysytyt kysymykset

Onko oppitunti ”UnsafePointer ja UnsafeMutablePointer” ilmainen?

Kyllä — voit lukea täällä verkossa kokonaan ilmaiseksi mitkä tahansa Swift Academy-oppimispolun 3 oppituntia, myös oppitunnin “UnsafePointer ja UnsafeMutablePointer”. Sen jälkeen CoddyKit PRO avaa kaikki oppitunnit sekä interaktiiviset harjoitukset sisäänrakennetulla koodieditorilla ja ympäri vuorokauden toimivalla tekoälytuutorilla. Swift Academy-kurssilla on yhteensä 4 oppituntia.

Mitä opin oppitunnilla ”UnsafePointer ja UnsafeMutablePointer”?

Lue ja kirjoita raakaa muistia huolellisesti. Harjoittelet Swift Academy-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.

Tarvitsenko kokemusta aloittaakseni Swift Academy-opiskelun?

Aiempi kokemus ei ole tarpeen. CoddyKitin Swift Academy-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 2/4.

Kuinka kauan ”UnsafePointer ja UnsafeMutablePointer”-oppitunnin suorittaminen kestää?

Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.

Voinko kirjoittaa ja suorittaa koodia tällä Swift Academy-oppitunnilla?

Kyllä. Jokainen Swift Academy-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.

Kaikki tämän kurssin oppitunnit

  1. MemoryLayout ja kohdistus
  2. UnsafePointer ja UnsafeMutablePointer
  3. Unsafe-puskuriosoittimet
  4. withUnsafeBytes ja C-yhteentoimivuus
← Takaisin: Swift Academy