Kodėl reikia sukurti „netiesiogiai neįtrauktus pasirinktinius“, nes tai reiškia, kad žinote, kokia yra ši vertė?

Kodėl reikia sukurti „Implicitly Unwrapped Optional“, palyginti su tik reguliaraus kintamojo ar pastovaus? Jei žinote, kad tai gali būti sėkmingai įdiegta, kodėl sukurti pasirinktinę parinktį? Pavyzdžiui, kodėl taip:

 let someString : String! = "this is the string" 

bus naudingesnis palyginti su:

 let someString : String = "this is the string" 

Jei "pasirinktiniai" rodo, kad konstanta ar kintamasis turi "neturi reikšmės", "bet" kartais iš programos struktūros aišku, kad pasirinkus šią vertę visada bus reikšminga ", kuri visų pirma padaro jį Jei žinote, kad pasirinktinis parametras visada yra svarbus ... Ar ne ... neprivaloma?

402
03 июня '14 в 7:09 2014-06-03 07:09 „ Johnston“ prisijungė birželio 03 d. 14 d., 07:09 2014-06-03 07:09
@ 8 atsakymai

Apsvarstykite objektą, kuris gali turėti nulines savybes, kai jis sukuriamas ir sukonfigūruojamas, bet po to jis yra nekintamas ir nulinis (NSImage dažnai traktuojama tokiu būdu, nors jo atveju ji vis dar naudinga kartais mutuoti). Netiesiogiai diegiamos galimybės gali žymiai pagerinti jūsų kodą, palyginti su nedideliu saugumo praradimu (su sąlyga, kad bus išsaugota viena iš garantijų, ji bus saugi).

(Redaguoti) Kad būtų aišku: reguliarios parinktys visada yra pageidautinos.

104
03 июня '14 в 7:15 2014-06-03 07:15 atsakymas pateikiamas Catfish_Man 03 birželio 14 d. 7:15 2014-06-03 07:15

Prieš aprašydamas netiesiogiai išpakuotų parinkčių naudojimo atvejus, jau turėtumėte suprasti, kokios parinktys ir netiesiogiai neįgalios parinktys yra „Swift“. Jei to nepadarysite, rekomenduoju pirmiausia perskaityti savo straipsnį apie parinktis.

Kada naudoti netiesiogiai išjungtą parinktį

Yra keturios pagrindinės priežastys, kodėl galima sukurti netiesiogiai neįtrauktą neprivalomą. Visi jie yra susiję su kintamojo apibrėžimu, kuris niekada nebus pasiekiamas, nes kitaip „Swift“ kompiliatorius visada priverčia jus aiškiai įvesti pasirinktinį parametrą.

1. konstanta, kuri negali būti nustatyta inicijavimo metu.

Kiekvienas nario konstantas turi turėti reikšmę po iniciacijos laiko. Kartais konstantas inicializuojant negali būti inicijuotas teisinga verte, tačiau gali būti garantuota, kad ji turės reikšmę prieš prieigą.

Pasirinktinio kintamojo naudojimas apeina šią problemą, nes pasirinktinis parametras automatiškai inicijuojamas nil , o vertė, kurią ji galiausiai turės, vis tiek bus tokia pati. Tačiau gali būti skausmas nuolat diegti kintamąjį, kurį tikrai žinote, o ne nulį. Netiesiogiai „Unwrapped Optionals“ turi tokias pačias lengvatas, kaip ir pasirinktinis, su papildoma nauda, ​​kurią jums nereikia aiškiai įdiegti.

Puikus pavyzdys yra tai, kad nario kintamasis negali būti inicijuojamas UIView poklasyje prieš įkeldami formą:

 class MyView : UIView { @IBOutlet var button : UIButton! var buttonOriginalWidth : CGFloat! override func awakeFromNib() { self.buttonOriginalWidth = self.button.frame.size.width } } 

Čia jūs negalite apskaičiuoti pradinio mygtuko pločio, kol bus awakeFromNib , bet žinote, kad awakeFromNib bus iškviestas prieš bet kurį kitą metodą (išskyrus inicijavimą). Vietoj to, kad priverstumėte vertę aiškiai išreikšti tiksliai visoje klasėje, galite ją paskelbti kaip netiesiogiai neįtrauktą pasirinktinai.

2. Sąveika su „Objective-C“ API

Kiekviena Objektų C objekto nuoroda yra rodyklė, o tai reiškia, kad jis gali būti nil . Tai reiškia, kad kiekviena sąveika su „Swift“ „Objective-C API“ turėtų naudoti parinktį, kurioje yra objekto nuoroda. Kiekvienu iš šių atvejų galite naudoti įprastą pasirinktinai, bet jei žinote, kad nuoroda nebus nil , galite išsaugoti savo išsiskleidžiančią kodą, deklaruodami ją kaip netiesiogiai neįtrauktą pasirinktinai.

Geras pavyzdys yra „ UITableViewDataSource :

EDIT: UITableViewDataSource pavyzdys nebegalioja. „Apple“ paaiškino API ir nė vienas iš parametrų nereikalingas ir nėra grįžimo vertė.

 override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell? { return nil } 

Čia jūs žinote, kad šis metodas niekada nebus iškviestas be tableView arba indexPath . Tai būtų laiko švaistymas, kad jį būtų galima patikrinti nil . Jei tai būtų tik „Swift“ API, ji nebūtų paskelbta kaip neprivaloma.

3. Kai jūsų programa negali atkurti iš nil kūrinio kintamojo.

Tai turėtų būti labai reti, tačiau, jei jūsų prašymas gali tiesiog neveikti, jei kintamasis yra nil kai jis pasiekiamas, tai bus laiko švaistymas, kad būtų išbandytas nil . Paprastai, jei turite sąlygą, kuri turi būti visiškai teisinga, kad jūsų prašymas tęstųsi, turėtumėte naudoti assert . Neprivaloma neįtraukta Neprivaloma turi tiesioginį įrašą nuliui.

4. NSObject iniciatoriai

„Apple“ turi bent vieną keistą atvejį, turintį netiesiogiai atleistų galimybių. Techniškai, visi iniciatoriai iš klasių, kurie paveldėjo iš NSObject grįžimo Implicitly Unwrapped Optionals. Taip yra dėl to, kad inicijavimas C-objekte gali grąžinti nil . Tai reiškia, kad kai kuriais atvejais vis tiek norite patikrinti inicijavimo rezultatą nil . Puikus pavyzdys yra UIImage jei vaizdas nėra:

 var image : UIImage? = UIImage(named: "NonExistentImage") if image != nil { println("image exists") } else { println("image does not exist") } 

Jei manote, kad yra tikimybė, kad jūsų įvaizdis neegzistuoja, ir jūs galite elegantiškai tvarkyti šį scenarijų, galite deklaruoti kintamąjį, nustatantį inicijavimą aiškiai kaip neprivalomą, kad galėtumėte ją patikrinti nil . Taip pat galite naudoti pasirinktinai pasirinktą neįpakuotą, bet kadangi jūs vis dar ketinate jį patikrinti, geriau naudoti įprastą pasirinktinį.

Jei nenaudojate netiesiogiai išjungtos parinkties

1. Laziliai apskaičiuoti narių kintamieji

Kartais jūs turite nario kintamąjį, kuris niekada neturėtų būti lygus nuliui, tačiau inicializavimo metu jis negali būti nustatytas teisinga verte. Vienas iš sprendimų yra naudoti „Implicitly Unwrapped“ (pasirinktinai), tačiau geriausias būdas yra naudoti tingų kintamąjį:

 class FileSystemItem { } class Directory : FileSystemItem { lazy var contents : [FileSystemItem] = { var loadedContents = [FileSystemItem]() // load contents and append to loadedContents return loadedContents }() } 

Dabar nario kintamojo contents nėra inicijuojamas prieš pirmą skambutį. Tai suteikia klasei galimybę įvesti teisingą būseną prieš apskaičiuojant pradinę vertę.

Pastaba: tai gali atrodyti priešingai nei pirmiau. Tačiau yra didelis skirtumas. buttonOriginalWidth aukščiau turi būti nustatytas buttonOriginalWidth , kad niekas buttonOriginalWidth mygtukų pločio prieš pasiekiant išteklių.

2. Kitur visur

Daugeliu atvejų turėtumėte vengti netiesiogiai neįgalių galimybių, nes jei jas naudosite klaidingai, jūsų paraiška bus sugadinta, kai ji bus pasiekta, ir nil . Jei niekada nežinote, ar kintamasis gali būti nulinis, visada naudokite įprastą numatytąją reikšmę. Perskaičiuojant kintamąjį, kuris niekada nėra nil , nėra daug žalos.

397
05 июля '14 в 8:03 2014-07-05 08:03 atsakymas pateikiamas dragas liepos 5 d. 14 d. 8:03 2014-07-05 08:03

Netiesiogiai išplėstos galimybės yra naudingos pateikiant turtą kaip neprivalomą, kai iš tikrųjų ji turėtų būti neprivaloma po viršeliais. Dažnai reikia „susieti mazgą“ tarp dviejų susijusių objektų, kurių kiekvienam reikia nuorodos į kitą. Tai prasminga, kai jokia nuoroda neprivaloma, tačiau vienas iš jų turi būti nulis, kol pora bus inicijuota.

Pavyzdžiui:

 // These classes are buddies that never go anywhere without each other class B { var name : String weak var myBuddyA : A! init(name : String) { self.name = name } } class A { var name : String var myBuddyB : B init(name : String) { self.name = name myBuddyB = B(name:"\(name) buddy B") myBuddyB.myBuddyA = self } } var a = A(name:"Big A") println(a.myBuddyB.name) // prints "Big A buddy B" 

Bet kuris B egzempliorius visada turi turėti tinkamą myBuddyA nuorodą, todėl nenorime, kad vartotojas jį peržiūrėtų kaip neprivalomą, tačiau mums reikia, kad jis būtų neprivalomas, kad galėtume sukurti B kol mes turėsime nuorodą A.

Tačiau! Šis abipusio ryšio reikalavimas dažnai rodo sunkų ryšį ir prastą dizainą. Jei atsidursite netiesiogiai įdiegtomis parinktimis, turbūt turėtumėte galvoti apie refaktoravimą, kad pašalintumėte tarpusavio priklausomybes.

51
04 июня '14 в 1:44 2014-06-04 01:44 atsakymas pateikiamas n8gray 04 Birželio 14 d. 1:44 2014-06-04 01:44

Netiesiogiai įdiegtos pasirinktys yra pragmatiškas kompromisas, kad darbas hibridinėje aplinkoje, kuri turi sąveikauti su esamais kakavos rėmeliais ir jų konvencijomis, būtų malonesnė, taip pat leistų laipsniškai pereiti prie saugesnės programavimo paradigmos - be „Swift“.

„Swift“ knyga skyriuje „Pagrindai“, „Netiesiogiai išjungtos parinktys“ :

Netiesiogiai išplėstos parinktys yra naudingos, kai patvirtinama, kad pasirinktinių vertybių vertė egzistuoja iškart po to, kai ji yra pasirinktinai apibrėžta, ir, be abejo, gali būti, kad jie egzistuoja kiekviename taške. Pirminis naudojimas netiesiogiai įdiegtoms parinktims „Swift“ klasėje inicijavimo metu, kaip aprašyta nenurodytose nuorodose ir netiesiogiai neįtrauktose papildomose ypatybėse .
...
Galite pateikti netiesiogiai įdiegtą parinktį, kuri suteikia galimybę automatiškai išplėsti parinktį, kai ji naudojama. Vietoj to, kad po pasirinkimo pavadinimo kiekvieną kartą, kai jį naudosite, po šauktuko užrašymo, po to, kai pasirenkate pasirinktinį variantą, nurodote šauktuką.

Taip atsitinka tais atvejais, kai nil savybės nustatomos naudojant naudojimo sutartį, ir kompiliatorius negali jį vykdyti per klasės inicijavimą. Pavyzdžiui, UIViewController savybės, inicijuotos iš NIB arba Storyboards, kuriose inicijavimas yra suskirstytas į atskiras fazes, bet po viewDidLoad() galite daryti prielaidą, kad ypatybės paprastai egzistuoja. Priešingu atveju, norint patenkinti kompiliatorių, turėjote naudoti priverstinį išplėtimą , pasirinktinį įpareigojimą arba pasirinktinę grandinę, kad paslėptumėte pagrindinį kodo tikslą.

Virš dalies knygos „Swift“ taip pat nurodo automatinio atskaitos skaičiavimo skyrių :

Tačiau yra trečiasis scenarijus, kai abi savybės visada turi turėti vertę, o po inicijavimo niekada neturėtų būti jokios savybės. Šiuo atveju naudinga derinti negaliojančią nuosavybę vienoje klasėje su netiesiogiai išplėstine neprivaloma nuosavybe kitoje klasėje.

Tai duoda abiejų savybių prieigą tiesiogiai (be papildomo diegimo), kai tik baigiama iniciacija, vengiant atskaitos kilpos.

Tai reiškia, kad jūs nesate šiukšlių surinkimo kalba, taigi jums, kaip programuotojui, ir netiesiogiai įdiegtoms parinktims pažeidimas yra įrankis, kuris slepia šią sielą.

Dėl „Kada naudoti kodą netiesiogiai išplėstas parinktis?“ klausimą. Kaip programų kūrėjas dažniausiai susiduriate su bibliotekos parašų metodais, parašytais „Objective-C“, kurie neturi galimybių išreikšti pasirinktinius tipus.

Nuo „ Swift“ su kakava ir „Objective-C“ skyriuje „Darbas su nuliu“ :

Kadangi „Objective-C“ negarantuoja jokių apribojimų, „Swift“ objektas leidžia importuoti „Objective-C“ API naudoti visas argumentų tipų klases ir grąžinimo tipus. Prieš naudodami objekto C objektą, turite įsitikinti, kad jis nėra.

Kai kuriais atvejais galite būti visiškai tikri, kad C-objekto metodas ar turtas niekada nepateikia nuorodos į nil objektą. Jei norite, kad šio specialiojo scenarijaus objektai būtų patogesni dirbti, „Swift“ importuoja objektų tipus kaip netiesiogiai išplėstas parinktis. Netiesiogiai išplėsti pasirinktiniai tipai apima visas papildomų tipų saugumo funkcijas. Be to, galite tiesiogiai pasiekti vertę netikrindami nil arba maksimaliai jį padidindami. Kai prieigą prie šio tipo neprivalomo tipo vertės, prieš tai nenaudodami saugiai, netiesiogiai išplėstinė parinktis tikrina, ar trūksta vertės. Jei trūksta vertės, įvyksta vykdymo klaida. Todėl, jei nesate tikri, kad trūksta vertės, visada turėtumėte patikrinti ir įdiegti netiesiogiai dislokuojamą parinktį.

... ir toliau gulėkite čia 2019

30
12 июня '14 в 17:06 2014-06-12 17:06 Palimondo atsakymą pateikė birželio 12 d. 14 d. 17:06 2014-06-12 17:06

Vienos eilutės (ar kelių eilučių) paprasti pavyzdžiai nėra labai gerai apibūdinami pasirinkimų elgesys - taip, jei deklaruojate kintamąjį ir iškart jį nurodote reikšme, neprivaloma.

Geriausias iki šiol matęs atvejis yra nustatymas, kuris atsiranda po to, kai objektas inicijuojamas, po to išplaukia, kad jis yra „garantuotas“ sekti šį nustatymą, pavyzdžiui. peržiūros valdiklyje:

 class MyViewController: UIViewController { var screenSize: CGSize? override func viewDidLoad { super.viewDidLoad() screenSize = view.frame.size } @IBAction printSize(sender: UIButton) { println("Screen size: \(screenSize!)") } } 

Žinome, kad printSize bus pakviestas įkėlus vaizdą - tai veiksmo metodas, prijungtas prie šio rodinio valdymo, ir mes to nekalbėjome. Taigi mes galime išsaugoti kai kuriuos neprivalomus čekius / sujungimus ! . „Swift“ negali atpažinti šios garantijos (bent jau tol, kol „Apple“ neišspręs sustabdymo problemos), todėl pasakykite kompiliatoriui, kad jis egzistuoja.

Tam tikru mastu tai sumažina saugumo lygį. Bet kurioje vietoje, kurioje yra netiesiogiai įdiegta parinktis, tai yra vieta, kur jūsų programa gali sugesti, jei jūsų „garantija“ ne visada bus įvykdyta, todėl ši funkcija naudojama taupiai. Taip pat naudojant ! visą laiką tai skamba kaip tu rėkia ir niekam nepatinka.

17
03 июня '14 в 7:26 2014-06-03 07:26 atsakymas pateikiamas „ Rickster“ birželio 03 d. 14 val. 7:26 2014-06-03 07:26

„Apple“ pateikia puikų pavyzdį „Swift“ programavimo sistemoje → Automatiniai atskaitos skaičiaiLeidžia tvirtus atskaitos ciklus tarp klasių pavyzdžiųNeišsiųstos nuorodos ir netiesiogiai išplėstos išplėstinės savybės

 class Country { let name: String var capitalCity: City! // Apple finally correct this line until 2.0 Prerelease (let -> var) init(name: String, capitalName: String) { self.name = name self.capitalCity = City(name: capitalName, country: self) } } class City { let name: String unowned let country: Country init(name: String, country: Country) { self.name = name self.country = country } } 

City “ iniciatorius vadinamas „ Country “ iniciatoriu. Tačiau „ Country iniciatorius negali perduoti „ City iniciatoriui, kol naujas „ Country “ egzempliorius nebus visiškai inicijuotas, kaip aprašyta „ Two-phase initialization“ .

Norėdami susidoroti su šiuo reikalavimu, capitalCity Country nuosavybę paskelbiate kaip netiesiogiai capitalCity neprivalomą nuosavybę.

14
17 авг. atsakymas pateikiamas fujianjin6471 17 rug . 2015-08-17 12:47 '15 at 12:47 2015-08-17 12:47

Jei žinote, kad vertė yra grąžinama iš pasirinktinės, o ne nil , „ Unwrapped Optionals“ Netiesiogiai negalite naudoti šių verčių iš parinkčių ir pasirinktinių parinkčių tiesioginiam surinkimui.

 //Optional string with a value let optionalString: String? = "This is an optional String" //Declaration of an Implicitly Unwrapped Optional String let implicitlyUnwrappedOptionalString: String! //Declaration of a non Optional String let nonOptionalString: String //Here you can catch the value of an optional implicitlyUnwrappedOptionalString = optionalString //Here you can't catch the value of an optional and this will cause an error nonOptionalString = optionalString 

Taigi tai yra skirtumas tarp naudojimo

let someString : String! ir let someString : String

2
14 янв. atsakymas suteiktas enadun 14 jan. 2017-01-14 19:52 '17, 7:52 pm 2017-01-14 19:52

Netiesioginių variantų pateisinimas yra lengviau paaiškinamas pirmiausia persvarstant diegimo įgyvendinimo priežastis.

Priverstinis neprivalomo (netiesioginio ar ne) panaudojimas! operatorius, reiškia, kad esate tikri, jog jūsų kodas neturi klaidų, ir parinktis jau turi vertę, kuria ji atsiskleidžia. Be! operatorius, tikriausiai tik prašote, kad privaloma:

  if let value = optionalWhichTotallyHasAValue { println("\(value)") } else { assert(false) } 

kuri nėra tokia gera

 println("\(value!)") 

Dabar netiesioginės pasirinktinės parinktys leidžia nurodyti parinktį, kurią, tikėtina, visada turėsite vertę išpakuodami į visus galimus srautus. Taigi jis tiesiog žengia žingsnį į priekį, padėdamas jums atsipalaiduoti rašymo reikalavimu! įdiegti kiekvieną kartą ir užtikrinti, kad vykdymo laikas vis dar būtų klaida, jei jūsų prielaidos apie srautą yra neteisingos.

2
04 июня '14 в 1:59 2014-06-04 01:59 atsakymas duotas Danra 04 birželio 14 d. 1:59 2014-06-04 01:59

Kiti klausimai apie žymų kūrimą arba Užduoti klausimą