Powoli zaczął się strach, gdy zdałem sobie sprawę, że zaczyna mi brakować tych zdjęć

Szybka + inicjalizacja z zamknięciami

Zamknięcia F.T.W.

Szybka uwaga - wszystkie moje przyszłe posty zostaną opublikowane na mojej dedykowanej stronie internetowej i ta publikacja nie jest już aktualizowana. Dziękuje za przeczytanie!

Zaczynam naprawdę kopać cały taniec inicjalizacyjny w Swift. Pisałem o tym. Napisałem o tym, dlaczego to działa tak, jak działa. Rozmawiałem o tym. Czytam o tym (dużo). Ale hej, wracam po jeszcze jeden akt w tej sprawie.

Spośród wielu pięknych i różnorodnych sposobów inicjowania czegoś w Swift - użycie zamknięć nie jest zwykle przedstawiane jako metoda, aby to zrobić. Ale, niestety, może sprawić, że kod inicjujący (plateplate) ™ będzie mniej bolesny i łatwiejszy w zarządzaniu.

Dla programistów obsługujących interfejs programisty - te dla Ciebie !

UIKit == UIHugeSetupCode ()

Słuchaj, to nie wina UIKits. Komponenty, z którymi użytkownik musi wchodzić w interakcję, nadają się do tworzenia kodu instalacyjnego, ponieważ są to preferencje. Zazwyczaj wiele z nich znajduje się w viewDidLoad lub loadView:

przesłonić func loadView ()
{
    niech helloWorldLbl = UILabel ()
    helloWorldLbl.text = NSLocalizedString („controller.topLbl.helloWorld”, komentarz: „Hello World!”)
    helloWorldLbl.font = UIFont.preferredFontForTextStyle (UIFontTextStyleBody)
    helloWorldLbl.textColor = UIColor.whiteColor ()
    helloWorldLbl.textAlignment = .Center
    self.view.addSubview (helloWorldLbl)
}

Jest to dość standardowe dla tych z nas, którzy ryzykują wody Cocoa Touch bez widoku .xib lub .storyboard. Chociaż, jeśli podzielasz moją miłość do maleńkich metod viewDidLoad lub loadView, możesz odłożyć to gdzie indziej.

Powiedzmy, właściwość:

letoWorldLbl: UILabel = {
    niech lbl = UILabel ()
    lbl.text = NSLocalizedString („controller.topLbl.helloWorld”, komentarz: „Hello World!”)
    lbl.font = UIFont.preferredFontForTextStyle (UIFontTextStyleBody)
    lbl.textColor = UIColor.whiteColor ()
    lbl.textAlignment = .Center
    zwróć lbl
} ()

Ładny. We własnej książce Apple o Swift zauważono, że „jeśli domyślna wartość twojej nieruchomości wymaga pewnego dostosowania lub konfiguracji, możesz użyć zamknięcia lub funkcji globalnej, aby zapewnić niestandardową wartość domyślną dla tej właściwości”. Jak już wspomnieliśmy, UIKit kontroluje wydajność dużo dostosowywania i konfiguracji.

Jednym z ładnych produktów ubocznych jest to, jak wygląda teraz loadView:

przesłonić func loadView
{
    self.view.addSubview (self.helloWorldLbl)
}

Zwróć jednak uwagę na „()” na końcu zamknięcia w deklaracji majątkowej. Dzięki temu małe kreatory Swift, które kompilują Twój kod, wiedzą, że instancja jest przypisywana do typu zwracanego zamknięcia. Gdybyśmy to pominęli, możliwe, że moglibyśmy przypisać faktyczne zamknięcie do instancji.

I w tym przypadku to jest .

Zasady to zasady

Mimo że mamy nową, błyszczącą zabawkę, koniecznie trzeba pamiętać o zasadach panujących w kraju. Ponieważ przypisujemy właściwość do zamknięcia, reszta jej zawierającej instancji mogła jeszcze nie zostać zainicjowana. Z tego powodu, gdy zamknięcie jest wykonywane - nie można odwoływać się do innych wartości właściwości ani siebie z tego poziomu.

Na przykład:

letoWorldLbl: UILabel = {
    niech lbl = UILabel ()
    lbl.text = self.someFunctionToDetermineText () // Błąd kompilatora
    lbl.font = UIFont.preferredFontForTextStyle (UIFontTextStyleBody)
    lbl.textColor = self.myAppTheme.textColor () // Kolejny błąd
    lbl.textAlignment = .Center
    zwróć lbl
} ()

Instancja self może nie być jeszcze bezpieczna w użyciu lub może się nie udać dzięki dwufazowemu procesowi inicjalizacji Swift. To samo dotyczy wszystkich właściwości instancji, które mogą, ale nie muszą być przydzielane i inicjowane, ponieważ zamknięcie jest wykonywane natychmiast.

Jest to wyraźna, ale uzasadniona wada przy stosowaniu zamknięć do inicjalizacji. Ma to jednak całkowity sens - i jest zgodne z jednym z trzech celów projektowych Swift: bezpieczeństwem.

Gettin ’Cute with Collections

Jedną z dziedzin, w której uważam tę technikę za szczególnie przydatną, są instancje reprezentujące jedną z wielu różnych form kolekcji w Swift. Spośród wielu talentów Swifta, krojenie i przeglądanie kolekcji z mocą tysiąca tytanów jest jednym z moich ulubionych.

Rozważ następujący przykład zaczerpnięty z inicjalizatora w projekcie, nad którym obecnie pracuję. Klasa, w której znajduje się ten kod, ma właściwość [Developer]. Przy nowym uruchomieniu ustawiam ich wartości początkowe z pliku .plist. Następnie są one przechowywane przez NSKeyedArchiver.

guard let devs = NSKeyedUnarchiver.unarchiveObjectWithFile (DevDataManager.ArchiveURL.path!) jako? [Developer] else
{
    self.developers = {
        niech pListData = // Uzyskaj dane plist
        var devArray: [Developer] = [Developer] ()
        // Skonfiguruj devArray na podstawie danych plist
        return devArray.map {$ 0.setLocation ()}
                       .filter {$ 0.isRentable}
                       .sort {$ 0.name <$ 1.name}
     } ()
    powrót
}
self.developers = devs

Podobało mi się to podejście, ponieważ chociaż nie używamy go poza inicjatorem, intencja kodu jest bardzo jasna, ponieważ odpowiada on tylko za ustawienie właściwości.

Gdy inicjalizatory i przesłonięcia viewDidLoad stają się większe, dzielenie takich rzeczy (przynajmniej) jest mile widzianym prezentem pod względem czytelności.

Pierwsze NSCute

Jeśli naprawdę kopiesz inicjowanie rzeczy z zamknięciem, ale cierpisz z powodu poważnego braku używania tych funkcjonalnych $ w kodzie, pociesz się. Korzystając z pewnego biegłego Swiftery, można napisać kod, który określa typ w samym zamknięciu, co daje pewną konfigurację w stylu pro. Rozważmy ten kod, który pierwszy raz natknąłem się na zawsze pouczający NSHipster:

@warn_unused_result
public func Init  (wartość: Type, @noescape block: (object: Type) -> Void) -> Type
{
    blok (obiekt: wartość)
    zwracana wartość
}

Podoba mi się dokąd to zmierza. Funkcja publiczna, która przyjmuje zamknięcie z typem obiektu za pomocą generics, który następnie zwraca ten typ. Oznacza to, że możesz się odwrócić i zainicjować rzeczy z większą ilością informacji o typie. Nasz pierwszy przykładowy kod z kolei wyglądałby następująco:

let helloWorldLbl = Init (UILabel ()) {
    $ 0.text = NSLocalizedString („controller.topLbl.helloWorld”, komentarz: „Hello World!”)
    $ 0.font = UIFont.preferredFontForTextStyle (UIFontTextStyleBody)
    $ 0.textColor = UIColor.whiteColor ()
    $ 0.textAlignment = .Center
}

Choć może się to wydawać fantazyjne, w rzeczywistości eliminuje potrzebę zmiennej instancji z poziomu zamknięcia i pozbywa się wymogu „()”. Bardzo miło .

Końcowe przemyślenia

Można powiedzieć, że przy użyciu takiej techniki jest sześć w jednej ręce, a pół tuzina w drugiej. Chociaż prawdą jest, że wiersze kodu autorstwa programisty pozostają w dużej mierze takie same, argumentowałbym, że jego rozmieszczenie i elastyczność sprawiają, że jest idealny do wielu scenariuszy.

To świetny sposób na załatwienie sprawy, a jest nawet kilka sposobów na zrobienie tego samego w naszym starym przyjacielu Objective-C. Ale hej, im więcej wiesz, amirite?

Aż do nextWeek = {let week = Week () week.advancedBy (dni: 7)} ()

Jordan Morgan jest inżynierem oprogramowania dla systemu iOS, który prowadzi Dreaming In Binary

Jeśli nauczyłeś się korzystać z zamknięć do inicjalizatorów, śmiało zachęcamy do NSRecommend (to, gdzie: poniżej);