Hvis du har jobbet med blokker i C eller Objective-C eller lambdas i Ruby, vil du ikke ha vanskelig å kaste hodet rundt begrepet lukninger. Lukking er ikke noe mer enn blokker av funksjonalitet som du kan passere rundt i koden din.
Faktisk har vi allerede jobbet med nedleggelser i de tidligere leksjonene. Det er riktig: funksjoner er også lukkene. La oss starte med det grunnleggende og inspisere anatomien til en lukking.
Som sagt, en nedleggelse er en blokk med funksjonalitet som du kan passere rundt i koden din. Du kan sende et lukke som et argument for en funksjon, eller du kan lagre den som en egenskap av en gjenstand. Lukker har mange brukstilfeller.
Navnet nedleggelse hint på en av de viktigste kjennetegn ved nedleggelser. En lukking fanger variablene og konstantene i konteksten der den er definert. Dette er noen ganger referert til som lukker over de variablene og konstantene. Vi kommer til å se på verdifotografering mer detaljert på slutten av denne leksjonen.
Du har allerede lært at funksjoner kan være utrolig kraftige og fleksible. Fordi funksjoner er lukninger, er lukninger like fleksible. I denne artikkelen oppdager du bare hvor fleksibel og kraftig de er.
C-programmeringsspråket har et lignende konsept, blokker. Lukker i Swift har imidlertid noen fordeler. En av de viktigste fordelene ved nedleggelser i Swift er at minnehåndtering er noe du, utvikleren, ikke trenger å bekymre deg for.
Selv behold sykluser, som ikke er uvanlige i C eller Objective-C, håndteres av Swift. Dette reduserer vanskelige å finne minnelekkasje eller krasj som skyldes ugyldige pekere.
Den grunnleggende syntaksen til en nedleggelse er ikke vanskelig, og det kan minne deg om globale og nestede funksjoner, som vi dekket tidligere i denne serien. Ta en titt på følgende eksempel.
(a: Int) -> Int i retur a + 1
Det første du legger merke til er at hele lukningen er pakket inn i et par krøllete bånd. Parametrene for lukkingen er innpakket i et par parenteser, skilt fra returtype av ->
symbol. Ovennevnte lukking aksepterer ett argument, en
, av type int
, og returnerer en int
. Lukkets kropp starter etter i
søkeord.
Navngitte nedleggelser, som er globale og nestede funksjoner, ser litt annerledes ut. Følgende eksempel skal illustrere forskjellene.
func increment (_ a: Int) -> Int return a + 1
De mest fremtredende forskjellene er bruken av func
søkeord og plasseringen av parametrene og returtypen. En lukking starter og slutter med en krøllet stav, innpakning av parametrene, returtype og lukkekroppen. Til tross for disse forskjellene, husk at hver funksjon er en nedleggelse. Ikke alle lukninger er en funksjon, skjønt.
Lukkene er kraftige, og følgende eksempel illustrerer hvor nyttig de kan være. I eksemplet lager vi en rekke stater. Vi påberoper kart(_:)
Funksjon på arrayet for å lage et nytt utvalg som bare inneholder de to første bokstavene i hver stat som en aktivert streng.
var stater = ["California", "New York", "Texas", "Alaska"] forkortetStates = states.map ((state: String) -> Strenge i la indeks = state.index (state.startIndex, offsetBy : 2) retur state.substring (til: indeks) .uppført ()) print (forkortetStater)
De kart(_:)
funksjon eller metode er vanlig for mange programmeringsspråk og biblioteker, for eksempel Ruby, PHP og JavaScript. I eksempelet ovenfor er det kart(_:)
funksjonen er påkalt på stater
array, forvandler innholdet, og returnerer et nytt array som inneholder de transformerte verdiene. Ikke bekymre deg for lukkets kropp for nå.
Tidligere i denne serien lærte vi at Swift er ganske smart. La meg vise deg nøyaktig hvor smart. Utvalget av stater er en rekke strenger. Fordi vi påberoper kart(_:)
Funksjon på array, Swift vet at stat
argumentet er av typen string
. Dette betyr at vi kan utelate typen, som vist i det oppdaterte eksemplet nedenfor.
la abbreviatedStates = states.map (state) -> String in let index = state.index (state.startIndex, offsetBy: 2) return state.substring (til: indeks) .uppercased ())
Det er noen flere ting vi kan utelate fra eksempelet ovenfor, noe som resulterer i følgende en-liner.
la abbreviatedStates = states.map (state in state.substring (til: state.index (state.startIndex, offsetBy: 2)). oppercased ())
La meg forklare hva som skjer.
Kompilatoren kan konkludere at vi returnerer en streng fra lukkingen vi overfører til kart(_:)
funksjon, noe som betyr at det ikke er noen grunn til å inkludere det i definisjonen for lukkede uttrykk.
Vi kan bare gjøre dette hvis lukkets kropp inneholder en enkelt setning, skjønt. I så fall kan vi sette setningen på samme linje som nedleggelsens definisjon, som vist i eksemplet ovenfor. Fordi det ikke er noen returtype i definisjonen og nr ->
symbolet som går foran returtypen, kan vi utelate parentesene som omslutter lukkeparametrene.
Det stopper ikke her, skjønt. Vi kan gjøre bruk av shorthand argument navn for å forenkle ovennevnte lukke uttrykk enda mer. Når du bruker et inline-lukkeuttrykk, som i eksempelet ovenfor, kan vi utelate listen over parametere, inkludert i
Søkeord som skiller parametrene fra lukkekroppen.
I lukkekroppen refererer vi argumentene ved å bruke stenografi-argumentnavn som Swift gir oss med. Det første argumentet er referert til av $ 0
, den andre ved $ 1
, etc.
I det oppdaterte eksemplet nedenfor har jeg utelatt listen over parametere og i
søkeord, og erstattet stat
argument i lukkets kropp med shorthand argumentet navnet $ 0
. Den resulterende utsagnet er mer konsistent uten å svekke lesbarheten.
la abbreviatedStates = states.map ($ 0.substring (til: $ 0.index ($ 0.startIndex, offsetBy: 2)). uppercased ())
Det swift programmeringsspråket definerer også et konsept kjent som baklukking. Ideen er enkel. Hvis du sender et lukke som det siste argumentet til en funksjon, kan du legge den lukke utenfor parentesen til funksjonssamtalen. Følgende eksempel viser hvordan dette virker.
la abbreviatedStates = states.map () $ 0.substring (til: $ 0.index ($ 0.startIndex, offsetBy: 2)). oppercased ()
Hvis det eneste argumentet til funksjonssamtalen er lukningen, er det til og med mulig å utelate parentesene i funksjonssamtalen.
la abbreviatedStates = states.map $ 0.substring (til: $ 0.index ($ 0.startIndex, offsetBy: 2)). oppercased ()
Merk at dette også fungerer for nedleggelser som inneholder flere setninger. Faktisk er det hovedårsaken til at etterspørselen er tilgjengelig i Swift. Hvis en lukning er lang eller kompleks, og det er det siste argumentet for en funksjon, er det ofte bedre å bruke syntaxen til avsluttende avslutning.
la abbreviatedStates = states.map (state) -> String i la indeks = state.index (state.startIndex, offsetBy: 2) return state.substring (til: indeks) .uppercased ()
Når du bruker nedleggelser, vil du ofte finne deg selv å bruke eller manipulere konstanter og variabler fra lukkens omgivende sammenheng i lukkets kropp. Dette kalles ofte verdivurdering. Det betyr bare at en lukking kan fange verdiene av konstanter og variabler fra konteksten der den er definert. Ta følgende eksempel for å bedre forstå begrepet verdiopptak.
func changeCase (store bokstaver: Bool, ofStrings strenger: String ...) -> [String] var newStrings = [String] () func changeToUppercase () for s i strenger newStrings.append (s.uppercased ()) func changeToLowerCase () for s i strenger newStrings.append (s.lowercased ()) hvis store bokstaver changeToUppercase () annet changeToLowerCase () returner newStrings la uppercasedStates = changeCase (store bokstaver: true, ofStrings: "California "," New York ") la lowercasedStates = changeCase (store bokstaver: false, ofStrings:" California "," New York ")
Jeg er sikker på at du er enig i det ovennevnte eksemplet er litt opptatt, men det viser tydelig hvordan verdifallingen fungerer i Swift. De nestede funksjonene, changeToUppercase ()
og changeToLowercase ()
, har tilgang til den ytre funksjonens argumenter, stater
, samt newStates
variabel erklært i ytre funksjon.
La meg forklare hva som skjer.
De changeCase (store bokstaver: ofStrings :)
funksjonen aksepterer en boolsk som sin første argument og en variadisk parameter av typen string
som sin andre parameter. Funksjonen returnerer en rekke strenger som består av strengene som er overført til funksjonen som det andre argumentet. I funksjonens kropp skaper vi et muterbart utvalg av strenger, newStrings
, der vi lagrer de modifiserte strengene.
De nestede funksjonene løper over strengene som sendes til changeCase (store bokstaver: ofStrings :)
funksjon og endre saken for hver streng. Som du kan se, har de direkte tilgang til strengene som sendes til changeCase (store bokstaver: ofStrings :)
funksjon så vel som newStrings
array, som er erklært i kroppen av changeCase (store bokstaver: ofStrings :)
funksjon.
Vi sjekker verdien av stor bokstav
, ring den aktuelle funksjonen, og returner newStrings
array. De to linjene på slutten av eksemplet viser hvordan changeCase (store bokstaver: ofStrings :)
funksjon fungerer.
Selv om jeg har demonstrert verdiopptak med funksjoner, husk at hver funksjon er en nedleggelse. Med andre ord gjelder samme regler for ikke navngitte nedleggelser.
Det er blitt nevnt flere ganger i denne artikkelen: funksjoner er nedleggelser. Det er tre typer nedleggelser:
Globale funksjoner, for eksempel print (_: separator: terminator :)
funksjon av Swift standardbiblioteket, ta ingen verdier. Nestede funksjoner har imidlertid tilgang til og kan fange verdiene av konstanter og verdier av funksjonen de er definert i. Det forrige eksempelet illustrerer dette konseptet.
Lukningsuttrykk, også kjent som navngitte lukninger, kan fange verdiene til konstanter og variabler av konteksten de er definert i. Dette ligner veldig på nestede funksjoner.
En lukking som fanger verdien av en variabel, er i stand til å endre verdien av den variabelen. Swift er klok nok til å vite om det skal kopiere eller referere til verdiene til konstanter og variabler det fanger.
Utviklere som lærer Swift og har liten erfaring med andre programmeringsspråk, vil ta denne oppførelsen for gitt. Det er imidlertid en viktig fordel at Swift forstår hvordan fangede verdier blir brukt i en lukning, og som et resultat kan håndtere minnehåndtering for oss.
Lukking er et viktig konsept, og du vil bruke dem ofte i Swift. De lar deg skrive fleksibel, dynamisk kode som er lett både å skrive og å forstå.
I neste artikkel undersøker vi objektorientert programmering i Swift, som starter med objekter, strukturer og klasser.
Hvis du vil lære å bruke Swift 3 for å kode avanserte funksjoner for virkelige apps, sjekk ut kurset vårt. Gå videre med Swift: Animasjon, Nettverk og Tilpassede kontroller. Følg med Markus Mühlberger som han koder en funksjonell iOS vær app med live vær data, tilpassede UI komponenter, og noen glatt animasjoner å bringe alt til liv.