, které podporují funkce první třídy, vám umožňují používat funkce a metody stejně jako jakýkoli jiný objekt nebo hodnota. Můžete je předat jako argumenty, uložit je do vlastností nebo je vrátit z jiné funkce. V pořadí slova, jazyk zachází s funkcemi jako s „občany první třídy“.

zatímco Swift je sotva prvním jazykem, který podporuje tento způsob manipulace s funkcemi, je to obvykle funkce, kterou vidíte v dynamičtějších jazycích, jako je JavaScript nebo Lua. Kombinace robustního statického systému Swiftu s prvotřídními funkcemi se tak stává docela zajímavou kombinací a může nám umožnit dělat docela kreativní věci.

Tento týden se podívejme na několik různých způsobů, jak mohou být prvotřídní funkce použity ve službě Swift!

Instabug: vyřešte chyby, pády a další problémy mnohem rychleji pomocí podrobných Stop zásobníku, síťových protokolů a událostí uživatelského rozhraní, které Instabug automaticky připojí ke každé zprávě o chybě. Používá mě i tisíce vývojových týmů iOS po celém světě. Vyzkoušejte to zdarma a integrujte jej pouze s jedním řádkem kódu.

předávání funkcí jako argumentů

začněme se základy. Protože funkce lze použít jako hodnoty, znamená to, že je můžeme předat jako argumenty. Řekněme například, že chceme do zobrazení přidat pole subviews. Normálně bychom mohli udělat něco takového:

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

výše uvedený kód funguje a není s ním nic špatného. Ale pokud využijeme funkcí první třídy, můžeme skutečně snížit jeho výřečnost docela hodně.

co můžeme udělat, je zacházet s metodou addSubview jako s uzavřením typu (UIView) -> Void (protože přijímá pohled, který chcete přidat, a nic nevrací). To dokonale odpovídá typu argumentu, který forEach přijímá (uzavření typu (Element) -> Void a v tomto případě typ Element je UIView). Výsledkem je, že můžeme view.addSubview předat přímo jako argument našemu volání forEach, jako je tento:

subviews.forEach(view.addSubview)

to je fakt hustý! 😎 Je však třeba mít na paměti, že při použití metod instance jako uzávěrů, jako je tento, si instanci automaticky ponecháte, pokud si uzávěr ponecháte. To vůbec není problém při předávání funkce jako neunikajícího argumentu uzavření, jako je výše, ale pro únik uzávěrů je třeba si uvědomit, aby se zabránilo opakování cyklů.

pro více informací o escaping vs non-escaping uzavření argumenty a zachycení, podívejte se na „zachycení objektů v Swift uzávěry“.

předávání inicializátorů jako argumentů

skvělá věc je, že to nejsou jen funkce a metody, které lze použít jako prvotřídní funkce ve Swiftu – můžete také použít inicializátory tímto způsobem.

řekněme například, že máme řadu obrázků, pro které bychom chtěli vytvořit zobrazení obrázků, a že chceme přidat každý z těchto zobrazení obrázků do zobrazení zásobníku. Pomocí funkcí první třídy můžeme dosáhnout všech výše uvedených pomocí jednoduchého řetězce map a forEach:

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

co se mi líbí na strukturování kódu tímto způsobem je, že se stává velmi deklarativní. Místo vnořených for smyček jednoduše deklarujeme, jaký chceme mít výsledek. Existuje samozřejmě rovnováha mezi deklarativním, kompaktním kódem a čitelností, ale pro jednoduché operace, jako je výše uvedené, si myslím, že využití prvotřídních funkcí může být super pěkné.

další příklady a případy použití pro předávání inicializátorů jako uzávěry najdete v „Simple Swift dependency injection with functions“ a „time traveling in Swift unit tests“.

vytváření odkazů na metody instance

pojďme se ponořit trochu hlouběji do nádherného světa prvotřídních funkcí 😉. Jedna věc, která mě nejdéle zmátla, byla skutečnost, že jsem dostal návrhy na automatické dokončení instance, když jsem chtěl zavolat statickou metodu. Zkuste zadat UIView. v Xcode vidět, co mám na mysli, dostanete každou metodu instance jako návrh 🤔.

Nejprve jsem si myslel, že se jedná o chybu Xcode, ale pak jsem se rozhodl to prozkoumat. Ukazuje se, že pro každou metodu instance, kterou typ má, existuje odpovídající statická metoda, která vám umožní načíst tuto metodu instance jako uzávěr předáním instance jako argumentu.

například můžeme použít následující k načtení odkazu na metodu removeFromSuperview pro danou instanci UIView :

let closure = UIView.removeFromSuperview(view)

volání výše uvedeného uzavření by bylo přesně stejné jako volání view.removeFromSuperview(), což je zajímavé, ale je to opravdu užitečné? Pojďme se podívat na několik scénářů, kde použití této funkce může ve skutečnosti vést k některým docela skvělým výsledkům.

Xtest na Linuxu

jedním ze způsobů, jak jeden z rámců společnosti Apple používá tuto funkci, je spuštění testů pomocí Xtest na Linuxu. Na vlastních platformách Apple XCTest pracuje pomocí Objective – C runtime k vyhledání všech testovacích metod pro daný testovací případ a poté je automaticky spustí. Nicméně, na Linuxu není Objective – C runtime, takže to vyžaduje, abychom napsat trochu boilerplate, aby se naše testy spustit.

nejprve musíme deklarovat statický slovník allTests, který obsahuje mapování mezi našimi názvy testů a skutečnými metodami, které se mají spustit:

poté předáme výše uvedený slovník funkci XCTMain pro spuštění našich testů:

XCTMain()

pod kapotou se používá funkce, která umožňuje extrahovat metody instance pomocí statického ekvivalentu, což nám umožňuje jednoduše odkazovat na funkce podle názvu ve statickém kontextu, zatímco stále umožňuje frameworku generovat metody instance ke spuštění. Docela chytré! 👍

bez této funkce bychom museli napsat něco takového:

volání metody instance na každý prvek v pořadí

Vezměme si tuto funkci na rotaci sami. Stejně jako jsme byli schopni předat metodu instance jiného objektu jako argument forEach, nebylo by skvělé, kdybychom mohli také předat metodu instance, kterou chceme, aby každý prvek v sekvenci provedl?

řekněme například, že máme řadu subviews, které chceme odstranit z jejich superview. Místo toho, aby to musel udělat:

for view in views { view.removeFromSuperview()}

nebylo by skvělé, kdybychom to místo toho mohli udělat:

views.forEach(UIView.removeFromSuperview)

dobrou zprávou je, že můžeme, vše, co musíme udělat, je vytvořit malé rozšíření na Sequence, které přijímá jednu z těchto staticky odkazovaných metod instance. Protože jsou to funkce, které generují funkci (Functionception! 😂 ) jejich typ bude vždy (Type) -> (Input) -> Output, takže pro naše rozšíření můžeme vytvořit přetížení forEach, které akceptuje uzavření takového typu:

Nyní můžeme snadno volat metody instance na každém členu libovolné sekvence! 🎉

implementace cíle / akce bez cíle-C

podívejme se na další příklad. V UIKit je vzorec cíl / akce velmi běžný, pro vše od pozorování kliknutí na tlačítka až po reakci na gesta. Osobně se mi tento vzor opravdu líbí, protože nám umožňuje snadno použít metodu instance jako zpětné volání, aniž bychom se museli starat o problém retain cycle, o kterém jsme hovořili dříve (při odkazování na metodu instance jako uzavření).

způsob implementace cíle/akce v Uikitu se však opírá o selektory Objective-C (proto musíte anotovat metody soukromé akce pomocí @objc). Řekněme, že jsme chtěli přidat vzorec cíl / akce k jednomu z našich vlastních názorů, a řekněme, že to chceme udělat, aniž bychom se spoléhali na selektory Objective-C. To by mohlo znít jako hodně práce a že to bude dělat věci strašně komplikované, ale díky prvotřídním funkcím-je to docela jednoduché! 😀

začněme definováním typu Action jako statické funkce, která vrací metodu instance pro daný typ a vstup:

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

dále vytvoříme náš pohled. Vytvoříme ColorPicker, které uživateli umožní vybrat barvu v aplikaci výkresu a přidat metodu pro přidání cílové akce &. Budeme sledovat všechna pozorování jako uzávěry a při každém spuštění uzávěru vygenerujeme metodu instance pro daný cíl a spustíme ji takto:

skvělá věc je, že můžeme skutečně použít funkce první třídy ještě výše. Použitím map API na Optional můžeme vygenerovat metodu instance a volat ji najednou, takto:

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

nakonec použijme naše nové target/action API v CanvasViewController, které představí naše ColorPicker. Stejně jako bychom přidali cílovou akci & do UIButton nebo UIGestureRecognizer, můžeme jednoduše předat samotný řadič zobrazení a spustit metodu instance, jako je tato:

zadejte bezpečné cíle & akce bez selektorů objektivních C nebo rizik pro únik paměti pomocí několika řádků kódu-docela v pohodě ! Support

podpora Swift od Sundell kontrolou tohoto sponzora:

Instabug: vyřešte chyby, pády a další problémy mnohem rychleji pomocí podrobných Stop zásobníku, síťových protokolů a událostí uživatelského rozhraní, které Instabug automaticky připojí ke každé zprávě o chybě. Používá mě i tisíce vývojových týmů iOS po celém světě. Vyzkoušejte to zdarma a integrujte jej pouze s jedním řádkem kódu.

závěr

funkce první třídy jsou velmi výkonnou funkcí. Tím, že budeme moci používat funkce a metody mnohem dynamičtějším způsobem, můžeme dosáhnout některých docela zajímavých výsledků a může to být opravdu užitečné při implementaci určitých druhů API.

nicméně, ve slavných slovech strýce Bena; s velkou mocí přichází velká odpovědnost. I když si myslím, že je velmi užitečné dozvědět se o těchto druzích funkcí a o tom, jak fungují, je také důležité při jejich používání vykonávat určitou zdrženlivost. Naším cílem by mělo být vždy vytvořit API, které jsou příjemné a snadno použitelné, a psát kód, který je snadno čitelný & udržovat. Funkce první třídy nám určitě mohou pomoci sloužit tomuto cíli,ale pokud se to vezme příliš daleko, může to také vést k opaku. Jako vždy, mým doporučením je experimentovat, vyzkoušejte tyto funkce a přesvědčte se sami, zda a jak je lze použít ve vašem vlastním kódu.

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna.