Demystifying @ escaping, @non-escaping, @ autoclosure i funkcja curry
zamknięcia to samodzielne bloki funkcjonalności, które mogą być przekazywane i używane w kodzie.
— Apple
Closures może przechwytywać i przechowywać odwołania do dowolnych stałych i zmiennych z kontekstu, w którym są zdefiniowane, znane jako closing over stay Closure. Możesz myśleć o zamknięciu jako o funkcji, która nie ma własnej nazwy i przechwytuje jakiekolwiek wartości z otoczenia. Funkcje i zamknięcia są obiektami pierwszej klasy w języku Swift: możesz je przechowywać, przekazywać jako argumenty do funkcji i traktować je tak, jak każdą inną wartość lub obiekt. Przekazywanie zamknięć jako procedur obsługi zakończenia jest częstym wzorem w wielu interfejsach API. Standardowa biblioteka Swift używa zamknięć głównie do obsługi zdarzeń i wywołań zwrotnych.
funkcje są samodzielnymi kawałkami kodu, które wykonują określone zadanie. Nadajesz funkcji nazwę, która identyfikuje to, co robi, i ta nazwa jest używana do „wywołania” funkcji, aby wykonać jej zadanie w razie potrzeby. Definiujesz funkcję za pomocą słowa kluczowego func
. Funkcje mogą przyjmować none do wielu parametrów, zmiennych parametrów i zwracać none lub wiele parametrów.
typy funkcji
Typ funkcji składa się z typów parametrów i typu zwracanego funkcji. Dla powyższego przykładu, typ funkcji to:(Int, Int) -> Int
można to odczytać jako: „funkcja, która ma dwa parametry, oba typu Int
i która zwraca wartość typu Int
.”Typ funkcji można ustawić jako parametr lub zwracać typ funkcji.
typy funkcji można przypisać do dowolnej zmiennej w ten sposób:
var mathFunction: (Int, Int) -> Int = add
funkcje są szczególnymi przypadkami zamknięcia. Zamknięcia przyjmują jedną z trzech form:
- funkcje globalne: mają nazwę i nie mogą przechwytywać wartości.
- funkcje zagnieżdżone: mają nazwę i mogą przechwytywać wartości z funkcji załączającej.
- wyrażenia zamknięcia: nie mają nazwy i mogą przechwytywać wartości z otaczającego ich kontekstu.
:
Zamknięcie można utworzyć, umieszczając typ funkcji wewnątrz nawiasów klamrowych i in
słowo kluczowe po typie zwrotnym.
skrócone nazwy argumentów
argumenty zamykające mogą odnosić się do pozycji, np. ,
,
,
i tak dalej.
niejawne Zwroty Z Zamknięcia:
jednokrotne zamknięcia mogą domyślnie zwracać wynik swojego pojedynczego wyrażenia, pomijając słowo kluczowe return
w swojej deklaracji.
w przypadku zamknięcia wyrażenia wielowierszowego nie można pominąć słowa kluczowego return
.
:
jeśli chcesz przekazać wyrażenie zamknięcia funkcji jako ostatni argument funkcji, a wyrażenie zamknięcia jest zbyt długie, można je zapisać jako końcowe zamknięcie. Końcowe zamknięcie jest zapisywane po nawiasach wywołania funkcji (), mimo że nadal jest argumentem funkcji. Kiedy używasz składni zamknięcia końcowego, nie zapisujesz etykiety argumentu zamknięcia jako części wywołania funkcji.
jeśli zamknięcie jest ostatnim parametrem metody, to swift pozwala na zapisanie w ten sposób 🖕
użycie końcowej składni zamknięcia starannie hermetyzuje funkcjonalność zamknięcia bezpośrednio po funkcji obsługiwanej przez closure, bez konieczności zawijania całego zamknięcia w zewnętrzne nawiasy metody reduce(_:)
.
Przechwytywanie wartości:
zamknięcie może przechwytywać stałe i zmienne z otaczającego kontekstu, w którym jest zdefiniowane. Zamknięcie może następnie odwoływać się i modyfikować wartości tych stałych i zmiennych z jego ciała, nawet jeśli pierwotny zakres, który zdefiniował stałe i zmienne, już nie istnieje.
w języku Swift najprostszą formą zamknięcia, która może przechwytywać wartości, jest funkcja zagnieżdżona, zapisana w ciele innej funkcji. Funkcja zagnieżdżona może przechwytywać dowolne argumenty swojej funkcji zewnętrznej, a także może przechwytywać dowolne stałe i zmienne zdefiniowane w funkcji zewnętrznej.
ta makeIncrementer
funkcja przyjmuje jeden argument tj. Int jako wejście i zwraca typ funkcji tj. () -> Int
. Oznacza to, że zwraca funkcję, a nie prostą wartość. Funkcja, którą zwraca, nie ma parametrów i za każdym razem zwraca wartość Int
.
tutaj amount
jest argumentem, runningTotal
jest zadeklarowana jako zmienna i zainicjalizowana za pomocą 0. Zagnieżdżona funkcja incrementer
przechwytuje amount
i runningTotal
z otaczającego kontekstu.
zobaczmy makeIncrementer
w akcji:
Uwaga: Jako optymalizacja, Swift może zamiast tego przechwytywać i przechowywać kopię wartości, jeśli ta wartość nie jest zmutowana przez zamknięcie i jeśli nie jest zmutowana po utworzeniu zamknięcia.
Swift obsługuje również wszystkie zarządzanie pamięcią związane z usuwaniem zmiennych, gdy nie są już potrzebne.
aby pozbyć się długiego wyrażenia zamknięcia w argumencie funkcji, możesz użyć typealias.
Nieujawniające się zamknięcia:
parametry zamknięcia były domyślnie wyłączone przed Swift 3. Zamknięcie nie ucieknie przed ciałem funkcji, jeśli parametry zamknięcia są oznaczone jako nie-uciekające
w Swift 3 zostało odwrócone. Kiedy przekazujesz closure jako argument funkcji, closure zostaje wykonane z ciałem funkcji i zwraca kompilator z powrotem. Gdy wykonanie się kończy, przekazane zamknięcie wychodzi poza zakres i nie ma już istnienia w pamięci.
najmniej, co musisz wiedzieć
parametry zamknięcia domyślnie nie są escaping, jeśli chcesz uciec przed wykonaniem zamknięcia, musisz użyć @escaping z parametrami zamknięcia.
1. Przekaż zamknięcie jako argument funkcji podczas wywołania funkcji.
2. Wykonaj pewną pracę w funkcji, a następnie wykonaj zamknięcie.
3. Funkcja zwraca.
ze względu na lepsze zarządzanie pamięcią i optymalizacje, Swift zmienił wszystkie zamknięcia, aby domyślnie nie uciekały. CaptureList.swift
jest przykładem zamknięcia nieinwazyjnego.
Uwaga :adnotacja @ non-escaping dotyczy tylko typów funkcji
Escaping Closures:
mówi się, że zamknięcie powoduje wyjście funkcji, gdy zamknięcie jest przekazywane jako argument do funkcji, ale jest wywoływane po powrocie funkcji. Oznaczanie zamknięcia za pomocą @escaping
oznacza, że musisz wyraźnie odwołać się do self
w obrębie zamknięcia.
1. Przekaż zamknięcie jako argument funkcji podczas wywołania funkcji.
2. Wykonaj dodatkową pracę w funkcji.
3. Funkcja wykonuje zamknięcie asynchronicznie lub zapisuje.
4. Funkcja zwraca.
zobaczmy, gdzie domyślnie są zamykane:
- zmienne typu funkcji są niejawnymi ucieczkami
- typealiazy są niejawnymi ucieczkami
- opcjonalne zamknięcia są niejawnymi ucieczkami
wspólny błąd:
Przypisywanie nie-uciekającego zamknięcia do zamykania ucieczki. Istnieją 2 sposoby, aby to naprawić:
- Oznacz zamknięcie jako wyjście ewakuacyjne
- lub zachowaj domyślne zachowanie @noescape, czyniąc zamknięcie opcjonalnym
Autoklosures:
atrybut Swift @autoclosure
umożliwia zdefiniowanie argumentu, który zostanie automatycznie zawinięty w Zamknięcie. Nie przyjmuje żadnych argumentów, a kiedy jest wywoływany, Zwraca wartość wyrażenia, które jest wewnątrz niego zawinięte. Ta wygoda składniowa pozwala pominąć nawiasy klamrowe wokół parametru funkcji, pisząc zwykłe wyrażenie zamiast jawnego zamknięcia.
na przykład funkcja assert(condition:message:file:line:)
pobiera autoklosure dla swoich parametrów condition
i message
; jej parametr condition
jest obliczany tylko w kompilacjach debugowania, a jej parametr message
jest obliczany tylko wtedy, gdy condition
jest false
.
func assert(_ expression: @autoclosure () -> Bool,
_ message: @autoclosure () -> String) {}
aby użyć @autoclosure
z @escaping
składnia atrybutów jest:
@autoclosure @escaping () -> Bool
zamknięcia vs bloki:
„szybkie zamykanie i bloki Objective-C są kompatybilne, więc możesz przekazywać szybkie zamykanie do metod Objective-C, które oczekują bloków. Szybkie zamknięcia i funkcje mają ten sam typ, dzięki czemu można nawet przekazać nazwę funkcji Swift. Zamknięcia mają podobną semantykę przechwytywania jak bloki, ale różnią się w jeden kluczowy sposób: zmienne są raczej mutowalne niż kopiowane. Innymi słowy, zachowanie _ _ block w Objective-C jest domyślnym zachowaniem zmiennych w Swift.”
rozwiązanie zależy od problemu. Co więcej, Apple przesuwa swój nacisk na wzorzec wywołania zwrotnego. UIAlertAction
jest tego przykładem.
https://medium.com/@abhimuralidharan/functional-swift-all-about-closures-310bc8af31dd
https://medium.com/@kumarpramod017/what-do-mean-escaping-and-nonescaping-closures-in-swift-d404d721f39d
https://oleb.net/blog/2016/10/optional-non-escaping-closures/
https://swiftunboxed.com/lang/closures-escaping-noescape-swift3/
https://medium.com/@johnsundell/using-autoclosure-when-designing-swift-apis-67fe20a8b2e