Swift From Scratch sluitingen

Als je met blokken in C of Objective-C of lambdas in Ruby hebt gewerkt, dan zul je je niet druk maken om het concept van sluitingen. Sluitingen zijn niets meer dan blokken met functionaliteit die u in uw code kunt doorgeven.

In feite hebben we in de vorige lessen al met sluitingen gewerkt. Dat klopt: functies zijn ook sluitingen. Laten we beginnen met de basis en de anatomie van een sluiting inspecteren.

1. Wat is een sluiting?

Zoals ik al zei, een afsluiting is een blok van functionaliteit dat je in je code kunt doorgeven. U kunt een afsluiting doorgeven als een argument van een functie of u kunt het opslaan als een eigenschap van een object. Sluitingen hebben veel gebruiksgevallen.

De naam sluiting verwijst naar een van de belangrijkste kenmerken van sluitingen. Een afsluiting vangt de variabelen en constanten van de context waarin deze is gedefinieerd. Dit wordt soms aangeduid als sluiten die variabelen en constanten. We gaan waardebepaling aan het einde van deze les gedetailleerder bekijken.

Flexibiliteit

Je hebt al geleerd dat functies ongelofelijk krachtig en flexibel kunnen zijn. Omdat functies sluitingen zijn, zijn sluitingen net zo flexibel. In dit artikel ontdek je hoe flexibel en krachtig ze zijn.

Geheugen management

De C-programmeertaal heeft een soortgelijk concept, blokken. Sluitingen in Swift hebben echter een aantal voordelen. Een van de belangrijkste voordelen van sluitingen in Swift is dat geheugenbeheer iets is waar u, de ontwikkelaar, zich geen zorgen over hoeft te maken.

Zelfs cycli behouden, die niet ongebruikelijk zijn in C of Objective-C, worden afgehandeld door Swift. Dit vermindert moeilijk te vinden geheugenlekken of crashes die worden veroorzaakt door ongeldige verwijzingen.

2. Syntax

De basissyntaxis van een afsluiting is niet moeilijk, en het kan u herinneren aan globale en geneste functies, die we eerder in deze serie hebben behandeld. Bekijk het volgende voorbeeld.

(a: Int) -> Int in ruil a + 1

Het eerste dat opvalt is dat de hele sluiting is omwikkeld met een paar accolades. De parameters van de sluiting zijn verpakt in een paar haakjes, gescheiden van het retourneertype door de -> symbool. De bovenstaande afsluiting accepteert één argument, een, van type Int, en retourneert een Int. Het lichaam van de sluiting begint na de in trefwoord.

Benoemde sluitingen, dat zijn globale en geneste functies, zien er een beetje anders uit. Het volgende voorbeeld zou de verschillen moeten illustreren.

func increment (_ a: Int) -> Int return a + 1

De meest opvallende verschillen zijn het gebruik van de func sleutelwoord en de positie van de parameters en retourneringstype. Een sluiting begint en eindigt met een gekrulde beugel, die de parameters, het retentietype en het sluitingslichaam omhult. Ondanks deze verschillen, onthoud dat elke functie een afsluiting is. Niet elke sluiting is echter een functie.

3. Sluitingen als parameters

Sluitingen zijn krachtig en het volgende voorbeeld illustreert hoe nuttig ze kunnen zijn. In het voorbeeld maken we een reeks toestanden. We roepen het kaart(_:) functie op de array om een ​​nieuwe array te maken die alleen de eerste twee letters van elke staat als een hoofdreeks bevat.

var states = ["California", "New York", "Texas", "Alaska"] laten afkortenStates = states.map ((state: String) -> String in let index = state.index (state.startIndex, offsetBy : 2) retourneer state.substring (naar: index) .uppercased ()) print (afgekorte statussen)

De kaart(_:) functie of methode is gebruikelijk in veel programmeertalen en bibliotheken, zoals Ruby, PHP en JavaScript. In het bovenstaande voorbeeld, de kaart(_:) functie wordt aangeroepen op de staten array, transformeert de inhoud ervan en retourneert een nieuwe array die de getransformeerde waarden bevat. Maak je voorlopig geen zorgen over het lichaam van de sluiting.

Type Inference

Eerder in deze serie hebben we geleerd dat Swift behoorlijk slim is. Laat me je precies laten zien hoe slim. De reeks statussen is een reeks tekenreeksen. Omdat we het kaart(_:) functie op de array, Swift weet dat het staat argument is van het type Draad. Dit betekent dat we het type kunnen weglaten, zoals wordt weergegeven in het bijgewerkte voorbeeld hieronder.

laat abbreviatedStates = states.map ((state) -> String in let index = state.index (state.startIndex, offsetBy: 2) return state.substring (to: index) .uppercased ())

Er zijn nog een paar dingen die we uit het bovenstaande voorbeeld kunnen weglaten, resulterend in de volgende one-liner.

laat abbreviatedStates = states.map (state in state.substring (to: state.index (state.startIndex, offsetBy: 2)). hoofdletters ())

Laat me uitleggen wat er gebeurt. 

De compiler kan concluderen dat we een string teruggeven van de sluiting die we doorgeven aan de kaart(_:) functie, wat betekent dat er geen reden is om deze op te nemen in de definitie van de afsluitingsuitdrukking. 

We kunnen dit alleen doen als het lichaam van de sluiting maar één statement bevat. In dat geval kunnen we die verklaring op dezelfde regel zetten als de definitie van de sluiting, zoals in het bovenstaande voorbeeld. Omdat er geen retourtype is in de definitie en nee -> symbool voorafgaand aan het retourneertype, kunnen we de haakjes weglaten die de parameters van de sluiting insluiten.

Shorthand-argumentennamen

Hier stopt het echter niet. We kunnen stenografische en argumentnamen gebruiken om de bovenstaande afsluitingsexpressie nog meer te vereenvoudigen. Wanneer u een inline-afsluitingsexpressie gebruikt, zoals in het bovenstaande voorbeeld, kunnen we de lijst met parameters weglaten, inclusief de in sleutelwoord dat de parameters van het sluitingslichaam scheidt.

In de afsluitende tekst verwijzen we naar de argumenten met behulp van stenografische en argumentnamen die Swift ons geeft. Het eerste argument wordt vermeld door $ 0, de tweede door $ 1, enz.

In het bijgewerkte voorbeeld hieronder heb ik de lijst met parameters weggelaten en de in sleutelwoord, en de vervangen staat argument in het lichaam van de sluiting met de afkorting en de naam van het argument $ 0. De resulterende verklaring is beknopter zonder de leesbaarheid in het gedrang te brengen.

laat abbreviatedStates = states.map ($ 0.substring (to: $ 0.index ($ 0.startIndex, offsetBy: 2)). hoofdletters ())

Trailing Closures

De Swift-programmeertaal definieert ook een concept dat bekend staat als trailing-sluitingen. Het idee is eenvoudig. Als u een afsluiting als het laatste argument van een functie doorgeeft, kunt u die sluiting buiten de haakjes van de functieaanroep plaatsen. Het volgende voorbeeld laat zien hoe dit werkt.

laat abbreviatedStates = states.map () $ 0.substring (to: $ 0.index ($ 0.startIndex, offsetBy: 2)). in hoofdletters ()

Als het enige argument van de functieaanroep de afsluiting is, is het zelfs mogelijk om de haakjes van de functieaanroep weg te laten.

laat abbreviatedStates = states.map $ 0.substring (to: $ 0.index ($ 0.startIndex, offsetBy: 2)). in hoofdletters ()

Merk op dat dit ook werkt voor sluitingen die meerdere instructies bevatten. Sterker nog, dat is de hoofdreden waarom trailing-sluitingen in Swift beschikbaar zijn. Als een afsluiting lang of complex is en dit het laatste argument van een functie is, is het vaak beter om de syntaxis van de trailing-afsluiting te gebruiken.

laat abbreviatedStates = states.map (state) -> String in let index = state.index (state.startIndex, offsetBy: 2) retourneer state.substring (naar: index) .uppercased ()

4. Waarden vastleggen

Wanneer u sluitingen gebruikt, merkt u vaak dat u constanten en variabelen uit de omringende context van de sluiting in de body van de sluiting gebruikt of manipuleert. Dit wordt vaak waardebepaling genoemd. Het betekent eenvoudigweg dat een afsluiting de waarden van constanten en variabelen kan vangen uit de context waarin deze is gedefinieerd. Neem het volgende voorbeeld om het concept van het vastleggen van waarden beter te begrijpen.

func changeCase (hoofdletters: Bool, ofStrings strings: String ...) -> [String] var newStrings = [String] () func changeToUppercase () for s in strings newStrings.append (s.uppercased ()) func changeToLowerCase () voor s in strings newStrings.append (s.lowercased ()) als hoofdletter changeToUppercase () else changeToLowerCase () return newStrings laat uppercasedStates = changeCase (hoofdletters: true, ofStrings: "California "," New York ") laat lowercasedStates = changeCase (hoofdletters: false, ofStrings:" California "," New York ")

Ik ben er zeker van dat u het ermee eens bent dat het bovenstaande voorbeeld een beetje gekunsteld is, maar het laat duidelijk zien hoe value capturing in Swift werkt. De geneste functies, changeToUppercase () en changeToLowercase (), toegang hebben tot de argumenten van de buitenfunctie, staten, net als de newStates variabele gedeclareerd in de buitenfunctie. 

Laat me uitleggen wat er gebeurt.

De changeCase (hoofdletters: ofStrings :) function accepteert een boolean als zijn eerste argument en een variadische parameter van het type Draad als zijn tweede parameter. De functie retourneert een array van strings bestaande uit de reeksen die aan de functie zijn doorgegeven als het tweede argument. In de body van de functie creëren we een veranderlijke array van strings, newStrings, waarin we de gewijzigde strings opslaan.

De geneste functies lus over de tekenreeksen die worden doorgegeven aan de changeCase (hoofdletters: ofStrings :) functie en verander het geval van elke reeks. Zoals je kunt zien, hebben ze directe toegang tot de reeksen die worden doorgegeven aan de changeCase (hoofdletters: ofStrings :) functie evenals de newStrings array, die wordt aangegeven in de body van de changeCase (hoofdletters: ofStrings :) functie.

We controleren de waarde van hoofdletters, roep de juiste functie op en retourneer de newStrings matrix. De twee regels aan het einde van het voorbeeld laten zien hoe het changeCase (hoofdletters: ofStrings :) functie werkt.

Ook al heb ik waarde-capturing met functies aangetoond, onthoud dat elke functie een afsluiting is. Met andere woorden, dezelfde regels zijn van toepassing op naamloze sluitingen.

sluitingen

Het is meerdere malen genoemd in dit artikel: functies zijn sluitingen. Er zijn drie soorten sluitingen:

  • globale functies
  • geneste functies
  • sluiting uitdrukkingen

Globale functies, zoals de drukken (_: separator: terminator :) functie van de Swift-standaardbibliotheek, geen waarden vastleggen. Geneste functies hebben echter toegang tot en kunnen de waarden vastleggen van constanten en waarden van de functie waarin ze zijn gedefinieerd. Het vorige voorbeeld illustreert dit concept.

Afsluitingsuitdrukkingen, ook wel naamloze sluitingen genoemd, kunnen de waarden van constanten en variabelen van de context waarin ze worden gedefinieerd, vastleggen. Dit lijkt veel op geneste functies.

Kopiëren en refereren

Een afsluiting die de waarde van een variabele vangt, kan de waarde van die variabele wijzigen. Swift is slim genoeg om te weten of het de waarden van de constanten en variabelen die het vastlegt moet kopiëren of verwijzen.

Ontwikkelaars die Swift leren en weinig ervaring hebben met andere programmeertalen, zullen dit gedrag als vanzelfsprekend beschouwen. Het is echter een belangrijk voordeel dat Swift begrijpt hoe vastgelegde waarden worden gebruikt bij een afsluiting en als gevolg daarvan geheugenbeheer voor ons kan verwerken.

Conclusie

Afsluitingen zijn een belangrijk concept en u zult ze vaak in Vlug gebruiken. Hiermee kunt u flexibele, dynamische code schrijven die gemakkelijk is om te schrijven en te begrijpen. 

In het volgende artikel verkennen we objectgericht programmeren in Swift, te beginnen met objecten, structuren en klassen.

Als je wilt leren hoe je Swift 3 kunt gebruiken om geavanceerde functies voor echte apps te coderen, bekijk dan onze cursus Go Further With Swift: Animation, Networking en Custom Controls. Volg samen met Markus Mühlberger terwijl hij een functionele iOS-weer-app codeert met live weersgegevens, aangepaste UI-componenten en een paar gelikte animaties om alles tot leven te brengen.