Articles

milloin käyttää heikkoa itseään ja miksi

Posted by admin

julkaissut donnywals 6. marraskuuta 2019november 6, 2019

me kaikki haluamme kirjoittaa hyvää, kaunista ja vakaata koodia. Tähän kuuluu muistivuotojen estäminen, minkä voimme, käyttäen kirjoitettaessa sulkua, joka tarvitsee pääsyn self: iin. Mutta mikä on todellinen syy tähän heikkoon kiinniottoon? Tarvitsemmeko sitä koko ajan? Tämän viikon Pikavihjeessä haluan auttaa sinua löytämään vastauksen tähän kysymykseen, jotta voit tehdä jatkossa entistä tietoisempia päätöksiä kaappauslistoistasi. Käymme ensin läpi kaappauslistat varmistaaksemme, että tiedät tarkalleen, mikä kiinniottolista on. Sitten tarkastelemme erilaisia kaappauksia ja lopuksi selitän, miten voit tehdä tietoon perustuvan päätöksen valitessasi sopivimman kaappauslistan mihin tahansa käyttötapaukseen.

ymmärtää, mikä kaappauslista on

kun kirjoitat sulkemisen, se tallentaa implisiittisesti kaikki sulkemisen sisällä viitatut ominaisuudet. Katsotaanpa lyhyt koodi näyte havainnollistaa tätä:

func multiply(by multiplier: Int) -> ((Int) -> Int) { return { (input: Int) -> Int in return input * multiplier }}var multiplier = 2let multiplyTwo = multiply(by: multiplier) // you can now call multiplyTwo as if it's a functionmultiplier = 4let multiplyFour = multiply(by: multiplier) // you can now call multiplyFour as if it's a function

edellinen koodinäyte näyttää funktion, joka ottaa kertoimen ja palauttaa sulkimen, joka ottaa eri syötön ja kertoo sen kertoimella, joka on välitetty arvoon multiply(by:). Funktiolta palautettava sulku kaappaa multiplier ja käyttää sitä antamasi syötön kertojana. Käytännössä tämä tarkoittaa, että soitto multiplyTwo(2) palauttaa 4 ja multiplyFour(2) palauttaa 8. Määritelty multiplier muuttuja ei vaikuta multiplyTwo: n tai multiplyFour: n sulkemiseen tämän kaappauskäyttäytymisen vuoksi.

tiedän, että tämä voi olla aika hämmentävää, kun olet juuri aloittanut sulkemisten opettelun, mutta koeta kestää, kun käymme läpi lisää esimerkkejä.

Katso seuraavaa esimerkkiä:

var name = "Donny"var appendToName = { (string: String) -> String in return name.appending(string)}let one = appendToName("Wals")name = "D"let two = appendToName("Wals")

mitä odottaisit arvojen one ja two olevan? Muista, että olen juuri selittänyt, miten sulkemiset kaapata ominaisuuksia, joita käytetään sisällä sulkeminen.

jos odotat one ja two molempien olevan "DonnyWals" en syytä sinua, siinä tuntuu olevan järkeä! Valitettavasti tämä ei pidä paikkaansa. one arvo on "DonnyWals" ja two on "DWals". Se, miten tämä sulkeminen eroaa aiemmin näkemästäsi sulkemisesta, on se, että kaikki sulkemisesta kiinteistöön, johon se viittaa, on samassa yhteydessä. Sulkeuma voi lukea nykyisen arvon name , koska se on samalla tasolla. name voidaan kuitenkin pyydystää pyydysluettelon avulla seuraavasti:

var name = "Donny"var appendToCapturedName = { (string: String) -> String in return name.appending(string)}let one = appendToCapturedName("Wals")name = "D"let two = appendToCapturedName("Wals")

kun käytät tätä koodia leikkikentällä, sekä one että two vastaavat "DonnyWals", koska olet nimenomaisesti määrännyt sulkemisen kuvaamaan name senhetkisen arvon laittamalla sen kaappausluetteloon. Tätä voidaan kutsua eksplisiittiseksi tai voimakkaaksi vangitsemiseksi. Kaappauslistaan voi napata useamman kuin yhden ominaisuuden pilkulla erottamalla ne: .

opit juuri voimakkaasta ja implisiittisestä ominaisuuksien kaappaamisesta. Katsotaan muunlaisia kaappauksia.

erilaisten kaappausten ymmärtäminen

kun vahvasti ottaa kiinni kiinteistön, sulkija omistaa tämän kiinteistön. Arvotyypeille, kuten struktuureille ja enumeille, tämä tarkoittaa, että sulkija kopioi objektin nykyisen arvon omalle muistialueelleen, jossa se omistaa objektin. Kun teet saman viitetyypille, kuten luokalle, sulkeminen säilyttää vahvan osoittimen viittauksen kohteeseen. Jotta ymmärtäisit tämän seuraukset, sinun on tiedettävä yksi asia viitetyypeistä.: niitä ei koskaan deallocated niin kauan kuin ainakin yksi muu esine on viittaus niihin. Kun esineessä on tahaton viittaus viitetyyppiin, sitä voidaan pitää muistivuotona. Jos et ole varma, mitä tämä tarkoittaa, älä huoli, sen pitäisi olla hieman selkeämpi seuraavan koodinäytteen jälkeen. Seuraava koodi osoittaa muistivuodon, jonka kuvailin aiemmin:

class MyClass {}var instance: MyClass? = MyClass()var aClosure = { in print(instance)}aClosure() // MyClassinstance = nilaClosure() // MyClass

toisella kerralla soitamme aClosure painamme edelleen saman instanssin MyClass, koska aClosure sisältää vahvan viittauksen instanssiin. Joskus tämä on juuri sitä, mitä haluamme, mutta yleensä, emme halua meidän sulkemiset pitää esineitä elossa, kun ne on deinitialisoitu. Esimerkki, joka tulee mieleen on sulkeminen, joka saattaa kaapata view-ohjain, kun se odottaa verkon pyynnön loppuun. Jos näkymäohjain hylätään ennen kuin verkko-pyyntö on valmis, haluamme, että näkymäohjain poistetaan muistista tai poistetaan. Jos verkkopyynnön loppuunsaattamisessa on vahva viittaus näkymäohjaimeen, sulkeminen pitää näkymäohjaimen elossa, koska siinä on edelleen viittaus näkymäohjaimeen.

Joten miten me tekisimme tästä vahvasta vangitsemisesta ei niin vahvan vangitsemisen? Mitä jos tekisimme siitä heikon?

class MyClass {}var instance: MyClass? = MyClass()var aClosure = { in print(instance)}aClosure() // MyClassinstance = nilaClosure() // nil

koska sulkemisessa on vain heikko viittaus esiintymään MyClass, järjestelmä ei laske sulkemisemme viittausta kohtaan instance, mikä tarkoittaa, että heti kun leikkikenttä julkaisee viittauksensa esiintymäämme MyClass, se voidaan poistaa. Huonona puolena tässä on se, että tämä saattaa johtaa hienovaraisiin bugeihin, joissa et heti huomaa, että instance oli deallocated ennen sulkemista kutsuttiin. Jos haluat väittää, että instance on vielä olemassa, kun sulkemista kutsutaan, ja haluat kaataa sovelluksesi, jos se ei ole, voit käyttää unowned – viitettä:

class MyClass {}var instance: MyClass? = MyClass()var aClosure = { in print(instance)}aClosure() // MyClassinstance = nilaClosure() // crash

unowned – kaappauksen vaikutus muistiin on jokseenkin sama kuin weak. Se on hyvin samanlainen kuin valinnaisen arvon purkaminen turvallisesti ?: llä tai sen tekeminen voimakkaasti !: llä, joka kaataa sovelluksen, jos avattava arvo on nil. Käytännössä huomaa, että unowned ei ole juuri koskaan sitä, mitä etsii.

nyt kun sinulla on jonkinlainen käsitys siitä, mitä weak ja unowned ovat, ja miten voit epäsuorasti tai voimakkaasti kaapata arvon, katsotaanpa, milloin sinun pitäisi käyttää näitä erilaisia kaappausmenetelmiä.

tietäen, milloin käyttää heikkoa, ei-omistettua, vahvaa tai implisiittistä kaappausta

kuten tämän Pikavinkin alussa mainittiin, monet kehittäjät käyttävät sulkemisissaan muistivuotojen estämiseksi. Mutta ovatko muistivuodot todella niin yleisiä sulkujen kanssa työskennellessä? Juonipaljastukset: eivät ole. Tässä osiossa voit ajatella weak ja unowned samoina, koska niiden suurin ero on siinä, kaatuvatko ne sovelluksesi, kun viitattu kohde on deallocated. Katsotaan ensin, milloin haluat käyttää epäsuoraa kiinniottoa. Sitten katsotaan vahvaa kiinniottoa ja viimeisenä heikkoa kiinniottoa.

kun käytetään implisiittistä kaappausta

implisiittistä kaappausta käytetään usein, kun on kyse sulkemisista, jotka kaappaavat self, missä self on arvotyyppi, kuten strukturaatio. Koska strukteissa ei ole osoittimia, jotka viittaisivat niihin, sulkemiset eivät vahingossa pidä struktuuria elossa pidempään kuin sen pitäisi. Itse asiassa, yrittää käyttää weak tai unowned Ei-luokan tyyppi ei ole sallittua Swift.

voit käyttää implisiittistä kaappausta viitetyypeissä, kunhan olet varma, ettei sulkemisen vastaanottava objekti säilytä kutsumaasi sulkua. Hyvä esimerkki tästä on työn tekeminen DispatchQueue:

class MyClass { func dispatchSomething() { DispatchQueue.global().async { // it's okay to implicitly capture self here } }}

koska sulkua async ei säilytetä, voit turvallisesti kaapata sen ilman kaappauslistaa. Sitä ei säilytetä, koska sulkeminen suoritetaan pian soiton jälkeen async, ja se soitetaan vain kerran. Jos sulkemisesi säilyy, esimerkiksi kun sitä käytetään jonkin esineen tapahtumankäsittelijänä, sinun tulee varmistaa, että kaappaat self heikosti, jotta et pidä viittausta itseen, jota et halua. Muista, että luotat usein toteutuksen yksityiskohtiin, kun teet tämän, joten vaikka sinun ei tarvitse käyttää weak self tässä, saattaa olla hyvä idea rajoittaa implisiittisten kaappausten käyttö koodiin, jonka omistat ja hallitset.

milloin käyttää vahvaa kaappausta

vahvasti kaappausviittauksia ei tehdä kovin usein. Se on hyödyllisintä, jos haluat sallia esineiden osittaisen deallocationin. Jos esimerkiksi suoritat verkkopyynnön ja haluat tallentaa sen tuloksen Ydintietosäilöön tekemättä mitään muuta, pyynnön käynnistäneen objektin ei tarvitse jäädä paikalle; tarvitset vain datasäilön. Esimerkki tästä voisi näyttää seuraavanlaiselta:

struct SomeDataSource { let storage: MyDataStore let networking: NetworkingLayer // more properties func refreshData() { networking.refresh { data in storage.persist(data) } }}

koska sulkeminen vaatii vain storage kiinteistön, ei ole tarvetta kaapata self täällä, koska se pitäisi koko SomeDataSource kohteen ympärillä.

heikon kaappauksen käyttö

heikko kaappaus on ylivoimaisesti yleisin kaappaus ja se on yleensä hyvä oletusvalinta. Sitä tulisi yleensä käyttää, kun et halua objektin pysyttelevän, kunnes sulkeminen on suoritettu, tai jos sulkemista säilytetään tuntematon määrä aikaa, joka on usein verkon pyyntöjä. Muista, että sulkeminen heikolla kiinniotolla käsittelee vangittua omaisuutta valinnaisena. Joten jos kaappauslistassasi on , haluat todennäköisesti avata self sulkemiskehossasi käyttäen guard strongSelf = self else { return } varmistaaksesi, että self on yhä olemassa sulkemisajankohtaan mennessä. weak: n ja unowned: n käyttäminen on ainoa tapa varmistaa 100%: sti, ettei viitetyyppisi ole muistissa pidempään kuin on tarpeen. Tämä tekee siitä hyvän vaihtoehdon, jos et ole varma, minkä tyyppinen kaappaus sopii parhaiten nykyiseen käyttötapaukseesi.

tiivistettynä

ja sekin Pikavihje, josta tuli paljon suurempi kuin alun perin tarkoitin. Sulkemisilla ja kaappauslistoilla on kaikenlaisia hienovaraisia vaikutuksia, jotka on tärkeää pitää mielessä ohjelmoinnissa ja toivon, että tämä viesti on antanut sinulle joitakin oivalluksia siitä, miksi joskus tarvitset viitetyypeille, mutta voit viitata self vapaasti arvotyypeissä (muista, kyse on viiteluvusta). Näit, että voit jopa kaapata kohteen tiettyjä ominaisuuksia, Jos sinun ei tarvitse vangita koko self. Voit jopa soveltaa weak tai unowned objektin yksittäisiin ominaisuuksiin estääksesi sulkemisia pitämästä yksittäisiä objekteja ympärillä.

nyt kun tiedät lähes kaiken mahdollisen sulkemisista ja kaappauslistoista, käy läpi koodikantasi nähdäksesi, onko mitään parannuksia, joita voit tehdä juuri lukemiesi tietojen perusteella. Kuten aina, kiitos lukemisesta ja älä epäröi ottaa yhteyttä Twitterissä, jos sinulla on palautetta tai kysymyksiä.

erityiskiitokset Bas Broekille tämän artikkelin oikoluvusta!

Pysy ajan tasalla viikkotiedotteestani

Practical Combine

Opi kaikki, mitä sinun tarvitsee tietää Combinesta ja miten voit käyttää sitä projekteissasi uuden kirjani Practical Combinen avulla. Saat kolmetoista lukua, leikkipaikka ja kourallinen esimerkkiprojekteja, joiden avulla pääset vauhtiin Combinen kanssa mahdollisimman pian.

kirja on saatavilla digitaalisena latauksena vain 29,99 dollarilla!

Get Practical Combine

Related Post

Leave A Comment