ensimmäisen luokan funktioita tukevat kielet mahdollistavat funktioiden ja menetelmien käytön aivan kuten minkä tahansa muun objektin tai arvon. Voit siirtää ne argumentteina, tallentaa ne ominaisuuksiin tai palauttaa ne toisesta funktiosta. Järjestyssanoissa kieli kohtelee funktioita ”ensimmäisen luokan kansalaisina”.

vaikka Swift tuskin on ensimmäinen kieli, joka tukee tätä tapaa käsitellä toimintoja, se on yleensä ominaisuus, jonka näkee dynaamisemmissa kielissä, kuten JavaScript tai Lua. Joten yhdistämällä Swiftin vankka staattinen tyyppi järjestelmä ensiluokkaisia toimintoja tulee varsin mielenkiintoinen yhdistelmä, ja voi antaa meille mahdollisuuden tehdä joitakin melko luovia asioita 😀.

tällä viikolla tutustutaan muutamaan eri tapaan, joilla ensiluokkaisia funktioita voi käyttää Swiftissä!

Instabug: ratkaise vikoja, kaatumisia ja muita ongelmia paljon nopeammin käyttämällä yksityiskohtaisia pinon jälkiä, verkkolokeja ja KÄYTTÖLIITTYMÄTAPAHTUMIA, jotka Instabug liittää automaattisesti kuhunkin vikailmoitukseen. Käyttää sekä minua että tuhansia iOS – kehitystiimejä ympäri maailmaa. Kokeile sitä ilmaiseksi ja integroida se vain yhdellä rivillä koodia.

Ohitusfunktiot argumentteina

aloitetaan perusasioista. Koska funktioita voidaan käyttää arvoina, se tarkoittaa, että voimme siirtää ne argumentteina. Esimerkiksi, sanotaan, että haluamme lisätä joukon alikatsauksia näkymä. Normaalisti voisimme tehdä jotain tällaista.:

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

yllä oleva koodi toimii, eikä siinä ole mitään vikaa. Mutta jos hyödynnämme ensimmäisen luokan toimintoja, voimme itse asiassa vähentää sen monisanaisuutta melko paljon.

addSubview – menetelmää voidaan pitää tyypin (UIView) -> Void sulkemisena (koska se hyväksyy näkymän lisättäväksi, eikä palauta mitään). Tämä vastaa täydellisesti argumentin tyyppiä, jonka forEach hyväksyy (tyypin sulkeminen (Element) -> Void, ja tässä tapauksessa Element tyyppi on UIView). Tuloksena on, että voimme siirtää view.addSubview suoraan argumenttina forEach – puheluumme, näin:

subviews.forEach(view.addSubview)

aika siistiä! 😎 Kuitenkin, yksi asia pitää mielessä, on, että kun käytät instanssi menetelmiä sulkemisia kuten tämä Olet automaattisesti säilyttää instanssi niin kauan kuin olet säilyttää sulkeminen. Tämä ei ole lainkaan ongelma, kun ohitetaan funktio ei-pakenevana sulkemisargumenttina, kuten edellä, mutta poistuvien sulkujen osalta on syytä olla tietoinen, jotta vältetään syklien säilyttäminen.

lisätietoja pakenemisen vs pakenemisen estämisen argumenteista ja kaappaamisesta löytyy kohdasta ”Capturing objects in Swift closures”.

initialisaattoreiden syöttäminen argumentteina

siistiä on se, että Swiftissä ei voi käyttää vain funktioita ja menetelmiä ykkösluokan funktioina – näin voi käyttää myös initialisaattoreita.

sanotaan esimerkiksi, että meillä on joukko kuvia, joille haluaisimme luoda kuvanäkymiä, ja että haluamme lisätä jokaisen näistä kuvanäkymistä pinonäkymään. Käyttämällä ensimmäisen luokan funktioita voimme saavuttaa kaikki edellä mainitut käyttämällä yksinkertaista ketjua map ja forEach:

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

pidän koodin jäsentämisestä tällä tavalla, – että siitä tulee hyvin deklaratiivinen. Sisäkkäisten for silmukoiden sijaan julistamme vain sen, mitä haluamme lopputuloksen olevan. Deklaratiivisen, kompaktin koodin ja luettavuuden välillä on tietenkin löydettävä tasapaino, mutta edellä mainittujen kaltaisten yksinkertaisten toimintojen hyödyntäminen voi mielestäni olla erittäin mukavaa.

lisää esimerkkejä ja käyttötapauksia initializereiden läpiviennistä sulkuina löytyy ”Simple Swift dependency injection with functions ”ja”Time traveling in Swift unit tests”.

Creating instance method references

Let ’ s dive a bit deeper into the wonderful world of first class functions 😉. Yksi asia, joka oli hämmentää minua pisimpään oli se, että sain esimerkiksi menetelmä automaattinen loppuun ehdotuksia, kun halusin soittaa staattinen menetelmä. Kokeile kirjoittaa UIView. Xcode nähdäksesi mitä tarkoitan, saat jokaisen instanssin menetelmän ehdotuksena 🤔.

Aluksi luulin tätä Xcode-bugiksi, mutta sitten päätin tutkia sitä. On käynyt ilmi, että kunkin instanssi menetelmä tyyppi on, siellä on vastaava staattinen menetelmä, jonka avulla voit hakea, että instanssi menetelmä sulkeminen, ohittamalla instanssi argumenttina.

esimerkiksi seuraavasta voidaan hakea viittaus removeFromSuperview – menetelmään tietylle UIView – esiintymälle:

let closure = UIView.removeFromSuperview(view)

edellä mainitun päätteen soittaminen olisi aivan sama kuin soittaisi view.removeFromSuperview(), mikä on mielenkiintoista, mutta onko siitä oikeasti hyötyä? Katsotaanpa katsomaan muutamia skenaarioita, joissa käyttämällä tätä ominaisuutta voi todella johtaa joitakin melko hienoja tuloksia.

Xctest Linuxissa

yksi tapa, jolla yksi Applen kehyksistä käyttää tätä ominaisuutta, on ajaa testejä Xctestillä Linuxissa. Applen omilla alustoilla XCTest toimii Objective – C-runtime-toiminnon avulla, joka etsii kaikki tietyn testitapauksen testimenetelmät ja suorittaa ne sitten automaattisesti. Linuxissa ei kuitenkaan ole Objective-C-ajonaikaa, joten se vaatii meitä kirjoittamaan hieman boilerplatea, jotta testimme sujuvat.

ensin on julistettava staattinen allTests sanakirja, joka sisältää kartoituksen testinimiemme ja varsinaisten suoritustapojen välillä:

siirrämme edellä mainitun sanakirjan XCTMain funktiolle testien suorittamiseksi:

XCTMain()

Hoodin alla tämä käyttää ominaisuutta, jossa instanssimenetelmiä voidaan poimia staattisella vastineellaan, jonka avulla voimme yksinkertaisesti viitata funktioihin nimeltä staattisessa kontekstissa, samalla kun kehys voi luoda instanssimenetelmiä suoritettavaksi. Aika nokkelaa! 👍

ilman tätä ominaisuutta olisimme joutuneet kirjoittamaan jotain tällaista:

kutsuen instanssimenetelmää jokaiselle alkuaineelle jonossa

otetaan tämä ominaisuus itse pyöräytykseen. Aivan kuten pystyimme läpäisemään toisen olion instanssimenetelmän argumenttina forEach, eikö olisi siistiä, jos voisimme myös läpäistä instanssimenetelmän, jonka haluamme jokaisen sekvenssin elementin suorittavan?

esimerkiksi, sanotaan, että meillä on joukko alikatsauksia, jotka haluamme poistaa heidän supervisionistaan. Sen sijaan, että joutuisit tekemään tämän:

for view in views { view.removeFromSuperview()}

eikö olisi siistiä, jos voisimme tehdä tämän sen sijaan?:

views.forEach(UIView.removeFromSuperview)

hyvä uutinen on, että voimme, meidän tarvitsee vain luoda pieni laajennus Sequence, joka hyväksyy yhden näistä staattisesti viitattu esimerkiksi menetelmiä. Koska ne ovat funktioita, jotka tuottavat funktion (Functionception! 😂 ) niiden Tyyppi tulee aina olemaan (Type) -> (Input) -> Output, joten laajennuksellemme voimme luoda forEach ylikuormituksen, joka hyväksyy tällaisen tyypin sulkemisen:

voimme nyt helposti kutsua instanssimenetelmiä minkä tahansa sekvenssin jokaiselle jäsenelle! 🎉

tavoite / toiminta ilman tavoitetta-C

katsotaanpa vielä yhtä esimerkkiä. Uikitissa kohde – / toimintamalli on hyvin yleinen, sillä kaikkea napin painallusten tarkkailusta eleisiin vastaamiseen. Itse pidän todella tätä mallia, koska sen avulla voimme helposti käyttää esimerkiksi menetelmää takaisinsoittona ilman huolta säilyttää syklin ongelma keskustelimme aiemmin (kun viitataan esimerkiksi menetelmä sulkeminen).

uikitissa target/action-toteutustapa nojaa kuitenkin tavoite-C-valintaluokkiin (tästä syystä yksityistoimintamenetelmät on kirjattava merkinnällä @objc). Oletetaan, että halusimme lisätä tavoite / toiminta kuvio yksi meidän mukautetun näkymiä, ja sanotaan, että haluamme tehdä sen luottamatta Tavoite-C valitsimet. Se saattaa kuulostaa paljon työtä ja että se tekee asiat hirveän monimutkainen, mutta kiitos ensiluokkaiset toiminnot – se on melko yksinkertainen! 😀

aloitetaan määrittelemällä Action typealias staattiseksi funktioksi, joka palauttaa instanssimenetelmän tietylle tyypille ja syötölle:

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

seuraavaksi luomme näkymämme. Luomme ColorPicker, jonka avulla käyttäjä voi valita piirtosovelluksessa värin ja lisätä siihen menetelmän, jolla kohde & – toiminto lisätään. Pidämme kirjaa kaikista havainnoista sulkemisina, ja joka kerta kun sulkemista suoritetaan, luomme instanssimenetelmän annetulle kohteelle ja suoritamme sen näin:

hienoa on, että voimme itse asiassa käyttää ensiluokkaisia funktioita vielä enemmän edellä. Käyttämällä map API: a Optional voimme luoda instanssimenetelmän ja kutsua sitä yhdellä kertaa, näin:

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

lopuksi käytetään uutta target/action API: a CanvasViewController, joka esittää meidän ColorPicker. Aivan kuten lisäämme kohteen & toiminto kohteeseen UIButton tai UIGestureRecognizer, voimme yksinkertaisesti ohittaa itse näkymäohjaimen ja instanssimenetelmän suoritettavaksi, näin:

Type safe targets & toiminnot ilman Objective-C-valitsimia tai riskejä muistivuodoille, käyttäen vain muutamaa riviä koodia-aika siistiä! 👍

Support Swift by Sundell by checking out this sponsor:

Instabug: ratkaise vikoja, kaatumisia ja muita ongelmia paljon nopeammin käyttämällä yksityiskohtaisia pinon jälkiä, verkkolokeja ja KÄYTTÖLIITTYMÄTAPAHTUMIA, jotka Instabug liittää automaattisesti kuhunkin vikailmoitukseen. Käyttää sekä minua että tuhansia iOS – kehitystiimejä ympäri maailmaa. Kokeile sitä ilmaiseksi ja integroida se vain yhdellä rivillä koodia.

johtopäätös

ensimmäisen luokan funktiot on hyvin voimakas ominaisuus. Käyttämällä toimintoja ja menetelmiä paljon dynaamisemmalla tavalla voimme saavuttaa melko mielenkiintoisia tuloksia, ja se voi olla todella hyödyllistä toteutettaessa tietynlaisia sovellusliittymiä.

kuitenkin Ben-sedän kuuluisin sanoin; suuren vallan mukana tulee suuri vastuu. Vaikka mielestäni on erittäin hyödyllistä oppia tällaisia ominaisuuksia ja miten ne toimivat, on myös tärkeää käyttää joitakin pidättyvyyttä, kun niitä käytetään. Tavoitteenamme tulisi aina olla luoda sovellusliittymiä, jotka ovat mukavia ja helppokäyttöisiä, sekä kirjoittaa koodia, joka on sekä helppolukuinen & ylläpitää. Ensiluokkaiset toiminnot voivat varmasti auttaa meitä saavuttamaan tämän tavoitteen, mutta jos ne viedään liian pitkälle, ne voivat johtaa myös aivan päinvastaiseen. Kuten aina, suositukseni on kokeilla, kokeilla näitä ominaisuuksia ja nähdä itse, jos ja miten niitä voidaan käyttää omassa koodissa.

Vastaa

Sähköpostiosoitettasi ei julkaista.