Swift de la zero închideri

Dacă ați lucrat cu blocuri în C sau Obiectiv-C sau lambda în Ruby, atunci nu veți avea dificultăți în a vă împacheta capul în jurul conceptului de închidere. Închiderea nu este altceva decât blocuri de funcționalitate pe care le puteți transmite în codul dvs..

De fapt, am lucrat deja cu închiderile din lecțiile anterioare. Asta-i drept: funcțiile sunt și închideri. Să începem cu elementele de bază și să inspectăm anatomia unei închideri.

1. Ce este o închidere?

După cum am spus, închiderea este un bloc de funcționalitate pe care îl puteți transmite în codul dvs. Puteți trece o închidere ca argument al unei funcții sau o puteți stoca ca proprietate a unui obiect. Închizăturile au multe cazuri de utilizare.

Numele închidere indică una dintre caracteristicile cheie ale închiderilor. O închidere captează variabilele și constantele contextului în care este definită. Aceasta este uneori menționată ca închidere acele variabile și constante. Vom vedea în detaliu captura de valoare la sfârșitul acestei lecții.

Flexibilitate

Ați învățat deja că aceste funcții pot fi incredibil de puternice și flexibile. Deoarece funcțiile sunt închideri, închiderile sunt la fel de flexibile. În acest articol, veți descoperi cât de flexibile și puternice sunt.

Gestionarea memoriei

Limba de programare C are un concept similar, blocuri. Închiderea în Swift are totuși câteva avantaje. Unul dintre avantajele cheie ale închiderilor în Swift este că managementul memoriei este ceva ce tu, dezvoltatorul, nu trebuie să-ți faci griji.

Chiar și ciclurile de reținere, care nu sunt neobișnuite în C sau în Obiectiv-C, sunt tratate de Swift. Acest lucru reduce greu de găsit scurgeri de memorie sau accidente care sunt cauzate de pointeri invalizi.

2. Sintaxa

Sintaxa de bază a unei închideri nu este dificilă și vă poate aminti funcțiile globale și imbricate pe care le-am acoperit mai devreme în această serie. Uitați-vă la următorul exemplu.

(a: Int) -> Int în schimb a + 1

Primul lucru pe care îl observați este că întreaga închidere este înfășurată într-o pereche de bretele curbate. Parametrii închiderii sunt înfășurate într - o pereche de paranteze, separate de tipul de retur prin -> simbol. Închiderea de mai sus acceptă un argument, A, de tip Int, și returnează un Int. Corpul închiderii pornește după în cuvinte cheie.

Închideri numite, care sunt funcții globale și imbricate, arată puțin diferit. Următorul exemplu ar trebui să ilustreze diferențele.

cresterea functiei (_ a: Int) -> Int return a + 1

Cele mai proeminente diferențe sunt utilizarea FUNC cuvântul cheie și poziția parametrilor și tipul returului. O închidere începe și se termină cu o bretonă curată, înfășurând parametrii, tipul de retur și corpul de închidere. În ciuda acestor diferențe, rețineți că fiecare funcție este o închidere. Nu orice închidere este însă o funcție.

3. Închiderea ca parametri

Închizăturile sunt puternice, iar exemplul următor ilustrează cât de utile pot fi acestea. În exemplu, creăm o serie de stări. Invocăm Hartă(_:) funcția pe matrice pentru a crea o matrice nouă care conține doar primele două litere ale fiecărei stări ca un șir capitalizat.

var Stari = ["California", "New York", "Texas", "Alaska"] lasati abbreviatStates = states.map ((state: String) -> String in let index = state.index (state.startIndex, offsetBy : 2) return state.substring (la: index) .uppercased ()) print (abbreviatedStates)

Hartă(_:) funcția sau metoda este comună pentru multe limbi de programare și biblioteci, cum ar fi Ruby, PHP și JavaScript. În exemplul de mai sus, Hartă(_:) funcția este invocată pe statele array, transformă conținutul său și returnează o nouă matrice care conține valorile transformate. Nu vă îngrijorați acum corpul închiderii.

Inferență de tip

Anterior, în această serie, am aflat că Swift este destul de inteligent. Lasă-mă să-ți arăt cât de inteligent. Gama de stări este o serie de șiruri de caractere. Pentru că noi invocăm Hartă(_:) funcția pe matrice, Swift știe că stat argumentul este de tip Şir. Aceasta înseamnă că putem omite tipul, așa cum se arată în exemplul actualizat de mai jos.

lasati abbreviatedStates = states.map ((state) -> String in let index = state.index (state.startIndex, offsetBy: 2) returnati state.substring (la: index) .uppercased

Există încă câteva lucruri pe care le putem omite din exemplul de mai sus, rezultând în următorul unic.

lăsați abbreviatedStates = states.map (state în state.substring (la: state.index (state.startIndex, offsetBy: 2)) uppercased ())

Permiteți-mi să vă explic ce se întâmplă. 

Compilatorul poate deduce că vom returna un șir de la închiderea pe care le trecem la Hartă(_:) funcția, ceea ce înseamnă că nu există niciun motiv să-l includem în definiția expresiei de închidere. 

Putem face acest lucru numai dacă corpul închiderii include o singură declarație. În acest caz, putem pune această afirmație pe aceeași linie cu definiția închiderii, după cum se arată în exemplul de mai sus. Deoarece nu există niciun tip de retur în definiție și nu -> simbolul care precede tipul de retur, putem omite parantezele care cuprind parametrii închiderii.

Nume de argument pentru stenograma

Dar nu se oprește aici. Putem folosi nume de argument de stenografie pentru a simplifica mai mult expresia de închidere de mai sus. Când folosim o expresie inline de închidere, ca în exemplul de mai sus, putem omite lista parametrilor, inclusiv în cuvânt cheie care separă parametrii de corpul de închidere.

În corpul de închidere, facem referire la argumentele care folosesc numele de argumentare a prescurtărilor pe care ni le oferă Swift. Primul argument este menționat de $ cu 0, al doilea prin $ de 1, etc.

În exemplul actualizat de mai jos, am omis lista parametrilor și în și a înlocuit cuvântul cheie stat argument în corpul închiderii cu numele argumentului stenografiei $ cu 0. Declarația rezultată este mai concisă, fără a compromite lizibilitatea.

permiteți abbreviatedStates = states.map ($ 0.substring (la: $ 0.index ($ 0.startIndex, offsetBy: 2)) uppercased ())

Trailing închideri

Limba de programare Swift definește, de asemenea, un concept cunoscut sub denumirea de închideri posterioare. Ideea este simplă. Dacă treceți o închidere ca ultimul argument al unei funcții, puteți plasa respectiva închidere în afara parantezelor apelului funcției. Următorul exemplu demonstrează modul în care funcționează acest lucru.

lasati abbreviatedStates = states.map () $ 0.substring (la: $ 0.index ($ 0.startIndex, offsetBy: 2)) uppercased ()

Dacă singurul argument al apelului funcției este închiderea, este posibil chiar să omiteți parantezele apelului funcției.

lasati abbreviatedStates = states.map $ 0.substring (la: $ 0.index ($ 0.startIndex, offsetBy: 2)) uppercased ()

Rețineți că acest lucru funcționează și pentru închiderile care conțin mai multe instrucțiuni. De fapt, acesta este principalul motiv pentru care închiderea este disponibilă în Swift. Dacă o închidere este lungă sau complexă și este ultimul argument al unei funcții, este adesea mai bine să utilizați sintaxa de închidere.

lasati abbreviatStates = state.map (state) -> String in let index = state.index (state.startIndex, offsetBy: 2) returnati state.substring (la: index) .uppercased ()

4. Capturarea valorilor

Atunci când utilizați dispozitive de închidere, veți găsi adesea că utilizați sau manipulați constante și variabile din contextul înconjurător al închiderii din corpul închiderii. Aceasta este adesea denumită captura de valoare. Pur și simplu înseamnă că o închidere poate capta valorile constantelor și variabilelor din contextul în care este definită. Luați exemplul de mai jos pentru a înțelege mai bine conceptul de captare a valorii.

func changeCase (majuscule: Bool, deStrings siruri de caractere: String ...) -> [String] var newStrings = [String] () func changeToUppercase () pentru s in strings newStrings.append (s.uppercased () dacă  () altfel changeToUppercase () else changeToLowerCase () returnează newStrings "," New York ") lăsăm mai joscasedStates = changeCase (majusculă: false, ofStrings:" California "," New York ")

Sunt sigur că sunteți de acord că exemplul de mai sus este un pic contrived, dar arată în mod clar modul în care captarea de valori funcționează în Swift. Funcțiile imbricate, changeToUppercase () și changeToLowercase (), au acces la argumentele funcției externe, statele, la fel de bine ca newStates variabilă declarată în funcția exterioară. 

Lasă-mă să explic ce se întâmplă.

changeCase (litere mari: ofStrings :) funcția acceptă un argument boolean ca prim argument și un parametru variadic de tip Şir ca al doilea parametru. Funcția returnează o serie de șiruri compuse din șiruri transferate funcției ca al doilea argument. În corpul funcției, vom crea o matrice mutabilă de șiruri de caractere, newStrings, în care stocăm șirurile modificate.

Buclele de funcții imbricate deasupra șirurilor care sunt transmise către changeCase (litere mari: ofStrings :) funcția și schimba cazul fiecărui șir. După cum puteți vedea, ei au acces direct la șirurile transmise către changeCase (litere mari: ofStrings :) precum și funcția newStrings array, care este declarat în corpul lui changeCase (litere mari: ofStrings :) funcţie.

Verificăm valoarea majuscule, apelați funcția corespunzătoare și returnați newStrings matrice. Cele două rânduri de la sfârșitul exemplului demonstrează cum changeCase (litere mari: ofStrings :) funcționează.

Chiar dacă am demonstrat că capturați valori cu funcții, amintiți-vă că fiecare funcție este o închidere. Cu alte cuvinte, aceleași reguli se aplică închiderilor fără nume.

închideri

A fost menționată de mai multe ori în acest articol: funcțiile sunt închideri. Există trei tipuri de închideri:

  • funcții globale
  • funcții imbricate
  • expresii de închidere

Funcțiile globale, cum ar fi Print (_: separator: terminator :) funcția bibliotecii standard Swift, nu capta nici o valoare. Funcțiile nespecificate au totuși acces la și pot capta valorile constantelor și valorilor funcției în care sunt definite. Exemplul anterior ilustrează acest concept.

Expresiile de închidere, cunoscute și sub denumirea de închideri fără nume, pot capta valorile constantelor și variabilelor contextului în care sunt definite. Acesta este foarte similar cu funcțiile imbricate.

Copierea și referirea

O închidere care captează valoarea unei variabile poate modifica valoarea acelei variabile. Swift este suficient de inteligent pentru a ști dacă ar trebui să copieze sau să facă referire la valorile constantelor și variabilelor pe care le captează.

Dezvoltatorii care învață Swift și au puțină experiență în alte limbi de programare vor considera acest comportament drept o excepție. Cu toate acestea, este un avantaj important faptul că Swift înțelege modul în care valorile capturate sunt utilizate într-o închidere și, ca rezultat, poate gestiona gestionarea memoriei pentru noi.

Concluzie

Închiderile sunt un concept important și le veți folosi adesea în Swift. Acestea vă permit să scrieți un cod flexibil dinamic care să fie ușor atât pentru a scrie cât și pentru a înțelege. 

În următorul articol, explorăm programarea orientată pe obiecte în Swift, pornind de la obiecte, structuri și clase.

Dacă doriți să aflați cum să utilizați tehnologia Swift 3 pentru a codifica funcțiile avansate pentru aplicațiile din lumea reală, consultați cursul Go Further With Swift: animație, rețea și comenzi personalizate. Urmați împreună cu Markus Mühlberger în timp ce codifică o aplicație funcțională iOS cu date meteorologice vii, componente UI personalizate și câteva animații slick pentru a aduce totul la viață.

Cod