sprog, der understøtter første klasses funktioner, giver dig mulighed for at bruge funktioner og metoder ligesom ethvert andet objekt eller værdi. Du kan videregive dem som argumenter, gemme dem i egenskaber eller returnere dem fra en anden funktion. I ordensord behandler sproget funktioner som”førsteklasses borgere”.

selvom hurtig næppe er det første sprog, der understøtter denne måde at håndtere funktioner på, er det normalt en funktion, du ser på mere dynamiske sprog som JavaScript eller Lua. Så at kombinere hurtig robust statisk type system med førsteklasses funktioner bliver en ganske interessant kombination, og kan give os mulighed for at gøre nogle ret kreative ting, som vi har.

denne uge, lad os tage et kig på et par forskellige måder, som førsteklasses funktioner kan bruges i hurtig!

Instabug: Løs fejl, nedbrud og andre problemer meget hurtigere ved hjælp af de detaljerede stakspor, netværkslogfiler og UI-begivenheder, som Instabug automatisk vedhæfter til hver fejlrapport. Brugt både af mig og tusindvis af iOS-udviklingshold rundt om i verden. Prøv det gratis og integrere det med blot en enkelt linje kode.

passerende funktioner som argumenter

lad os starte med det grundlæggende. Da funktioner kan bruges som værdier, betyder det, at vi kan videregive dem som argumenter. Lad os for eksempel sige, at vi vil tilføje en række undervisninger til en visning. Normalt, vi kan gøre noget som dette:

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

ovenstående kode fungerer, og der er ikke noget galt med det. Men hvis vi drager fordel af førsteklasses funktioner, kan vi faktisk reducere dens verbositet ganske meget.

hvad vi kan gøre er at behandle addSubview – metoden som en lukning af typen (UIView) -> Void (da den accepterer en visning for at tilføje og ikke returnerer noget). Dette passer perfekt til den type argument, som forEach accepterer (en lukning af typen (Element) -> Void, og i dette tilfælde er Element typen UIView). Resultatet er, at vi kan videregive view.addSubview direkte som et argument til vores forEach opkald, som dette:

subviews.forEach(view.addSubview)

det er ret sejt! Men en ting at huske på er, at når du bruger instansmetoder som lukninger som denne, beholder du automatisk forekomsten, så længe du bevarer lukningen. Dette er slet ikke et problem, når man passerer en funktion som et ikke-flygtende lukningsargument som ovenfor, men for at undslippe lukninger er det noget at være opmærksom på for at undgå at bevare cyklusser.

For mere information om escaping vs ikke-escaping lukning argumenter og opfange, tjek “capture objekter i hurtige lukninger”.

Passing initialisatorer som argumenter

det seje er, at det ikke kun er funktioner og metoder, der kan bruges som førsteklasses funktioner i Hurtig – du kan også bruge initialisatorer på denne måde.

lad os for eksempel sige, at vi har en række billeder, som vi gerne vil oprette billedvisninger til, og at vi vil tilføje hver af disse billedvisninger til en stakvisning. Ved hjælp af førsteklasses funktioner kan vi opnå alt det ovenstående ved hjælp af en simpel kæde på map og forEach:

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

hvad jeg kan lide ved at strukturere kode på denne måde er, at det bliver meget erklærende. I stedet for indlejrede for sløjfer erklærer vi simpelthen, hvad vi ønsker, at resultatet skal være. Der er selvfølgelig en balance mellem deklarativ, kompakt kode og læsbarhed, men for enkle operationer som ovenstående tror jeg at udnytte førsteklasses funktioner kan være super flot.

du kan finde flere eksempler og bruge sager til at passere initialisatorer som lukninger i “enkel hurtig afhængighedsinjektion med funktioner” og “tidsrejser i hurtige enhedstest”.

oprettelse af instansmetodereferencer

lad os dykke lidt dybere ned i den vidunderlige verden af førsteklasses funktioner prist. En ting, der forvirrede mig i længst tid, var det faktum, at jeg fik forslag til automatisk færdiggørelse af instansmetode, da jeg ville kalde en statisk metode. Prøv at skrive UIView. i kode for at se, hvad jeg mener, du får hver forekomstmetode som et forslag.

først troede jeg, at dette var en kodefejl, men så besluttede jeg at undersøge det. Det viser sig, at for hver instans metode en type har, der er en tilsvarende statisk metode, der lader dig hente denne instans metode som en lukning, ved at passere en instans som et argument.

for eksempel kan vi bruge følgende til at hente en henvisning til removeFromSuperview – metoden for en given UIView forekomst:

let closure = UIView.removeFromSuperview(view)

at kalde ovenstående lukning ville være nøjagtigt det samme som at ringe view.removeFromSuperview(), hvilket er interessant, men er det virkelig nyttigt? Lad os se på et par scenarier, hvor brug af denne funktion faktisk kan føre til nogle ret seje resultater.


en måde at en af Apples rammer bruger denne funktion er, når du kører tests ved hjælp af . På Apples egne platforme fungerer ved at bruge Objective-C runtime til at slå alle testmetoder op for en given testsag og kører dem derefter automatisk. Der er dog ingen Objective-C runtime, så det kræver, at vi skriver lidt kedelplade for at få vores test til at køre.

først skal vi erklære en statisk allTests ordbog, der indeholder en kortlægning mellem vores testnavne og de faktiske metoder til at køre:

vi sender derefter ovenstående ordbog til funktionen XCTMain for at køre vores tests:

XCTMain()

under hætten bruger dette funktionen til at kunne udtrække instansmetoder ved hjælp af dens statiske ækvivalent, hvilket gør det muligt for os blot at henvise til funktionerne ved navn i en statisk sammenhæng, mens det stadig gør det muligt for rammen at generere instansmetoder til at køre. Temmelig klog!

uden denne funktion ville vi have været nødt til at skrive noget som dette:

kalder en instansmetode på hvert element i en sekvens

lad os tage denne funktion til et spin selv. Ligesom vi var i stand til at videregive et andet objekts instansmetode som et argument til forEach, ville det ikke være sejt, hvis vi også kunne passere en instansmetode, som vi ønsker, at hvert element i en sekvens skal udføre?

lad os for eksempel sige, at vi har en række undervisninger, som vi vil fjerne fra deres overvågning. I stedet for at skulle gøre dette:

for view in views { view.removeFromSuperview()}

ville det ikke være sejt, hvis vi kunne gøre dette i stedet:

views.forEach(UIView.removeFromSuperview)

den gode nyhed er, at vi kan, alt hvad vi skal gøre er at oprette en lille udvidelse på Sequence, der accepterer en af disse statisk refererede instansmetoder. Da de er funktioner, der genererer en funktion (Functionception! deres type vil altid være (Type) -> (Input) -> Output, så for vores udvidelse kan vi oprette en forEach overbelastning, der accepterer en lukning af en sådan type:

vi kan nu nemt kalde instansmetoder på hvert medlem af en hvilken som helst sekvens! Ret

implementering af mål/handling uden mål-C

lad os se på endnu et eksempel. I UIKit er mål – / handlingsmønsteret meget almindeligt for alt fra at observere knapklik til at reagere på bevægelser. Jeg personligt kan virkelig godt lide dette mønster, da det giver os mulighed for nemt at bruge en instansmetode som tilbagekald uden at skulle bekymre os om det tilbageholdelsescyklusproblem, vi diskuterede tidligere (når der henvises til en instansmetode som lukning).

den måde, mål/handling implementeres i UIKit, er dog afhængig af Objective-C-vælgere (derfor skal du kommentere private handlingsmetoder med @objc). Lad os sige, at vi ønskede at tilføje mål/handlingsmønster til en af vores brugerdefinerede visninger, og lad os sige, at vi vil gøre det uden at stole på Objective-C-vælgere. Det lyder måske som en masse arbejde, og at det vil gøre tingene forfærdeligt komplicerede, men takket være førsteklasses funktioner – det er ret simpelt!

lad os starte med at definere en Action typealias som en statisk funktion, der returnerer en forekomstmetode for en given type og input:

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

lad os derefter skabe vores syn. Vi opretter en ColorPicker, der lader brugeren vælge en farve i en tegningsapp og tilføje en metode til at tilføje et mål & handling til det. Vi holder styr på alle observationer som lukninger, og hver gang en lukning køres, genererer vi en forekomstmetode for det givne mål og kører det sådan:

det seje er, at vi faktisk kan bruge førsteklasses funktioner endnu mere ovenfor. Ved at bruge map API på Optional kan vi generere forekomstmetoden og kalde den på en gang som denne:

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

endelig, lad os bruge vores nye mål/handling API i en CanvasViewController, som vil præsentere vores ColorPicker. Ligesom vi ville tilføje et mål & handling til en UIButton eller UIGestureRecognizer, kan vi simpelthen passere selve visningskontrolleren og en instansmetode til at køre, sådan her:

skriv sikre mål & handlinger uden nogen Objective-C – vælgere eller risici for hukommelseslækager ved hjælp af blot et par kodelinjer-ret cool! Hr.

Støtte Hurtig af Sundell ved at tjekke denne sponsor:

Instabug: Løs fejl, nedbrud og andre problemer meget hurtigere ved hjælp af de detaljerede stakspor, netværkslogfiler og UI-begivenheder, som Instabug automatisk vedhæfter til hver fejlrapport. Brugt både af mig og tusindvis af iOS-udviklingshold rundt om i verden. Prøv det gratis og integrere det med blot en enkelt linje kode.

konklusion

førsteklasses funktioner er en meget kraftfuld funktion. Ved at kunne bruge funktioner og metoder på en meget mere dynamisk måde kan vi opnå nogle ret interessante resultater, og det kan være virkelig nyttigt, når vi implementerer visse typer API ‘ er.

men i de berømte ord onkel Ben; med stor magt kommer stort ansvar. Selvom jeg synes, det er super nyttigt at lære om denne slags funktioner, og hvordan de fungerer, er det også vigtigt at udvise en vis tilbageholdenhed, når du bruger dem. Vores mål bør altid være at skabe API ‘ er, der er rart og nemt at bruge, og at skrive kode, der er både let at læse & vedligeholde. Førsteklasses funktioner kan helt sikkert hjælpe os med at tjene dette mål, men hvis det tages for langt, kan det også føre til det modsatte. Som altid er min anbefaling at eksperimentere, prøve disse funktioner og se selv, om og hvordan de kan bruges i din egen kode.

Skriv et svar

Din e-mailadresse vil ikke blive publiceret.