Språk som støtter førsteklasses funksjoner, gjør at du kan bruke funksjoner og metoder akkurat som alle andre objekter eller verdier. Du kan sende dem som argumenter, lagre dem i egenskaper eller returnere dem fra en annen funksjon. I ordord behandler språket funksjoner som «førsteklasses borgere».

Selv Om Swift ikke er det første språket som støtter denne måten å håndtere funksjoner på, er Det normalt en funksjon du ser på mer dynamiske språk som JavaScript eller Lua. Så å kombinere Swifts robuste statiske type system med førsteklasses funksjoner blir en ganske interessant kombinasjon, og kan tillate oss å gjøre noen ganske kreative ting 😀.

Denne uken, la Oss ta en titt på noen forskjellige måter som førsteklasses funksjoner kan brukes I Swift!

Instabug: Løs feil, krasjer og andre problemer mye raskere ved hjelp av detaljerte stakkspor, nettverkslogger og UI-hendelser som Instabug automatisk legger til hver feilrapport. Brukes både av meg og tusenvis av iOS utviklingsteam rundt om i verden. Prøv det gratis og integrere det med bare en enkelt linje med kode.

Passerer funksjoner som argumenter

la oss starte med det grunnleggende. Siden funksjoner kan brukes som verdier, betyr det at vi kan sende dem som argumenter. La oss for eksempel si at vi vil legge til en rekke undervisninger i en visning. Normalt, vi kan gjøre noe sånt som dette:

let subviews = subviews.forEach { subview in view.addSubview(subview)}

ovennevnte kode fungerer, og det er ingenting galt med det. Men hvis vi utnytter førsteklasses funksjoner, kan vi faktisk redusere verbositeten ganske mye.

det vi kan gjøre er å behandle metoden addSubview som en lukning av typen (UIView) -> Void( siden den aksepterer en visning for å legge til, og ikke returnerer noe). Dette passer perfekt til typen argument som forEach aksepterer(en lukning av typen (Element) -> Void, og i dette tilfellet er Elementtypen UIView). Resultatet er at vi kan passere view.addSubview direkte som et argument til vår forEach samtale, slik:

subviews.forEach(view.addSubview)

det er ganske kult! En ting å huske på er imidlertid at når du bruker instansmetoder som nedleggelser som dette, beholder du automatisk forekomsten så lenge du beholder lukningen. Dette er ikke et problem i det hele tatt når du passerer en funksjon som et ikke-escaping closure argument som ovenfor, men for å unnslippe nedleggelser er det noe å være klar over for å unngå å beholde sykluser.

for mer informasjon om escaping vs non-escaping closure argumenter og fange, sjekk ut «Fange objekter I Swift closures».

Passerer initializers som argumenter

det kule er at det ikke bare er funksjoner og metoder som kan brukes som førsteklasses funksjoner I Swift – du kan også bruke initializers på denne måten.

la oss for eksempel si at vi har en rekke bilder som vi ønsker å lage bildevisninger for, og at vi vil legge til hver av disse bildevisningene i en stabelvisning. Ved å bruke førsteklasses funksjoner kan vi oppnå alt ovenfor ved hjelp av en enkel kjede på map og forEach:

images.map(UIImageView.init) .forEach(stackView.addArrangedSubview)

det jeg liker med å strukturere kode på denne måten er at det blir veldig deklarativt. I stedet for nestede for looper erklærer vi bare hva vi vil at utfallet skal være. Det er selvfølgelig en balanse mellom deklarativ, kompakt kode og lesbarhet, men for enkle operasjoner som ovenfor tror jeg å dra nytte av førsteklasses funksjoner kan være super fin.

du kan finne flere eksempler og brukstilfeller for å passere initialisatorer som nedleggelser i «Simple Swift dependency injection with functions» og»time traveling in Swift unit tests».

Opprette instansmetodereferanser

la oss dykke litt dypere inn i den fantastiske verden av førsteklasses funksjoner 😉. En ting som var forvirrende meg for lengst var det faktum at jeg fikk eksempel metode auto ferdigstillelse forslag når jeg ønsket å kalle en statisk metode. Prøv å skrive UIView. I Xcode for å se hva Jeg mener, du får alle forekomstmetoder som et forslag 🤔.

først trodde jeg at dette var En Xcode-feil, men da bestemte jeg meg for å undersøke det. Det viser seg at for hver forekomstmetode en type har, er det en tilsvarende statisk metode som lar deg hente den forekomstmetoden som en lukning, ved å sende en forekomst som et argument.

vi kan for eksempel bruke følgende til å hente en referanse til removeFromSuperview – metoden for en gitt UIView – forekomst:

let closure = UIView.removeFromSuperview(view)

Å Ringe ovennevnte nedleggelse ville være akkurat det samme som å ringe view.removeFromSuperview(), noe som er interessant, men er det veldig nyttig? La oss ta en titt på noen scenarier der bruk av denne funksjonen faktisk kan føre til noen ganske kule resultater.

XCTest På Linux

En måte Som En Av Apples rammer bruker denne funksjonen, er når du kjører tester ved Hjelp Av XCTest På Linux. På Apples egne plattformer fungerer XCTest ved å bruke Objective-C runtime for å slå opp alle testmetoder for et gitt testfall, og kjører dem automatisk. Men På Linux er det ingen Objective-C runtime, så det krever at vi skriver litt standardtekst for å få testene våre til å kjøre.

først må vi deklarere en statisk allTests ordbok som inneholder en kartlegging mellom testnavnene våre og de faktiske metodene for å kjøre:

vi sender deretter ordlisten til XCTMain – funksjonen for å kjøre testene våre:

XCTMain()

Under hetten bruker dette funksjonen til å kunne trekke ut forekomstmetoder ved hjelp av sin statiske ekvivalent, noe som gjør det mulig for oss å bare referere til funksjonene ved navn i en statisk kontekst, samtidig som rammen gjør det mulig å generere forekomstmetoder for å kjøre. Ganske smart! 👍

Uten denne funksjonen ville vi måtte skrive noe slikt:

Kalle en forekomstmetode på hvert element i en sekvens

La oss ta denne funksjonen for et spinn selv. Akkurat som vi kunne passere et annet objekts forekomstmetode som et argument til forEach, ville det ikke vært kult hvis vi også kunne passere en forekomstmetode som vi vil at hvert element i en sekvens skal utføre?

la oss for eksempel si at vi har en rekke undervisninger som vi vil fjerne fra deres superview. I stedet for å måtte gjøre dette:

for view in views { view.removeFromSuperview()}

Ville det ikke vært kult om vi kunne gjøre dette i stedet:

views.forEach(UIView.removeFromSuperview)

Den gode nyheten er at vi kan, alt vi trenger å gjøre er å lage en liten utvidelse på Sequence som aksepterer en av disse statisk refererte forekomstmetodene. Siden de er funksjoner som genererer en funksjon (Functionception! 😂 ) deres type vil alltid være (Type) -> (Input) -> Output, så for vår utvidelse kan vi opprette en forEach overbelastning som aksepterer en lukning av en slik type:

Vi kan nå enkelt ringe instansmetoder på hvert medlem av en hvilken som helst sekvens! 🎉

Implementering av mål / handling uten Mål-C

La oss ta en titt på et annet eksempel. I UIKit er målet / handlingsmønsteret svært vanlig, for alt fra å observere knappeklikk til å svare på bevegelser. Jeg personlig liker dette mønsteret, siden det lar oss enkelt bruke en forekomstmetode som en tilbakeringing uten å måtte bekymre seg for beholdningssyklusproblemet vi diskuterte tidligere (når vi refererte til en forekomstmetode som en lukning).

måten mål / handling er implementert i UIKit, er imidlertid avhengig Av Objective-C-velgere(derfor må du annotere private handlingsmetoder med @objc). La oss si at vi ønsket å legge til mål / handlingsmønster til en av våre tilpassede visninger, og la oss si at vi vil gjøre det uten å stole på Objective-C-velgere. Det kan høres ut som mye arbeid, og at det vil gjøre ting veldig komplisert, men takket være førsteklasses funksjoner-det er ganske enkelt! 😀

la oss begynne med å definere en Action typealias som en statisk funksjon som returnerer en forekomstmetode for en gitt type og inngang:

typealias Action<Type, Input> = (Type) -> (Input) -> Void

Neste, la oss lage vårt syn. Vi lager en ColorPicker som lar brukeren velge en farge i en tegneapp, og legge til en metode for å legge til et mål & – handling. Vi holder styr på alle observasjoner som nedleggelser, og hver gang en lukning kjøres, genererer vi en forekomstmetode for det gitte målet og kjører det, slik:

det kule er at vi faktisk kan bruke førsteklasses funksjoner enda mer over. VED å bruke map API på Optional, kan vi generere forekomstmetoden og kalle den på en gang, slik:

observations.append { view in target.map(action)?(view)}

Til Slutt, la oss bruke vår nye target / action API i en CanvasViewController, som vil presentere vår ColorPicker. Akkurat som vi ville legge til et mål & – tiltak til en UIButton eller UIGestureRecognizer, kan vi ganske enkelt passere visningskontrolleren selv og en forekomstmetode for å kjøre, slik:

Skriv inn sikre mål & handlinger uten Objective-C-velgere eller risiko for minnelekkasjer, ved hjelp av bare noen få linjer med kode – ganske kult! 👍

Støtte Swift Av Sundell ved å sjekke ut denne sponsoren:

Instabug: Løs feil, krasjer og andre problemer mye raskere ved hjelp av detaljerte stakkspor, nettverkslogger og UI-hendelser som Instabug automatisk legger til hver feilrapport. Brukes både av meg og tusenvis av iOS utviklingsteam rundt om i verden. Prøv det gratis og integrere det med bare en enkelt linje med kode.

Konklusjon

Førsteklasses funksjoner er en veldig kraftig funksjon. Ved å kunne bruke funksjoner og metoder på en mye mer dynamisk måte kan vi oppnå noen ganske interessante resultater, og det kan være veldig nyttig når du implementerer visse Typer Apier.

Men I De berømte Ordene Til Onkel Ben; med stor makt kommer stort ansvar. Mens jeg synes det er super nyttig å lære om slike funksjoner og hvordan de fungerer, er det også viktig å utøve litt selvbeherskelse når du bruker dem. Vårt mål bør alltid være å lage Apier som er fine og enkle å bruke, og å skrive kode som både er lett å lese & vedlikeholde. Førsteklasses funksjoner kan definitivt hjelpe oss med å tjene det målet, men hvis det tas for langt, kan det også føre til det motsatte. Som alltid er min anbefaling å eksperimentere, prøv disse funksjonene og se selv om og hvordan de kan brukes i din egen kode.

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert.