Die Angst setzte nur langsam ein, als mir klar wurde, dass mir langsam diese Bilder ausgehen

Swift + Initialisierung mit Closures

Verschlüsse F.T.W.

Ein kurzer Hinweis: Alle meine zukünftigen Beiträge werden auf meiner speziellen Website veröffentlicht, und diese Veröffentlichung wird nicht mehr aktualisiert. Danke fürs Lesen!

Ich fange an, den gesamten Initialisierungstanz in Swift wirklich zu verinnerlichen. Ich habe darüber geschrieben. Ich schrieb darüber, warum es überhaupt so funktioniert. Ich habe darüber geredet. Ich habe darüber gelesen (viel). Aber hey, ich bin zurück, um noch etwas zu unternehmen.

Unter all den vielen, schönen und vielfältigen Möglichkeiten, mit denen man in Swift etwas initialisieren kann, wird die Verwendung von Verschlüssen normalerweise nicht als Methode dafür genannt. Aber leider kann es boilerplatey ™ init () - Code viel weniger schmerzhaft und ein wenig leichter handhabbar machen.

Für die programmatische Benutzeroberfläche gibt es Entwickler - diese für Sie!

UIKit == UIHugeSetupCode ()

Es ist kein Fehler von UIKits. Komponenten, mit denen ein Benutzer interagieren muss, bieten sich aufgrund von Einstellungen für eine Vielzahl von Setup-Codes an. In der Regel befindet sich ein Großteil davon entweder in viewDidLoad oder loadView:

überschreibe func loadView ()
{
    let helloWorldLbl = UILabel ()
    helloWorldLbl.text = NSLocalizedString ("controller.topLbl.helloWorld", Kommentar: "Hello World!")
    helloWorldLbl.font = UIFont.preferredFontForTextStyle (UIFontTextStyleBody)
    helloWorldLbl.textColor = UIColor.whiteColor ()
    helloWorldLbl.textAlignment = .Center
    self.view.addSubview (helloWorldLbl)
}

Dies ist ziemlich normal für diejenigen von uns, die das Cocoa Touch-Gewässer ohne .xib oder .storyboard in Sichtweite wagen. Wenn Sie meine Vorliebe für winzige viewDidLoad- oder loadView-Methoden teilen, können Sie dies jedoch an anderer Stelle verschieben.

Sagen wir, eine Eigenschaft:

let helloWorldLbl: UILabel = {
    let lbl = UILabel ()
    lbl.text = NSLocalizedString ("controller.topLbl.helloWorld", Kommentar: "Hello World!")
    lbl.font = UIFont.preferredFontForTextStyle (UIFontTextStyleBody)
    lbl.textColor = UIColor.whiteColor ()
    lbl.textAlignment = .Center
    return lbl
} ()

Ziemlich. In Apples eigenem Buch über Swift heißt es: „Wenn der Standardwert Ihrer Eigenschaft eine Anpassung oder Einrichtung erfordert, können Sie einen Abschluss oder eine globale Funktion verwenden, um einen benutzerdefinierten Standardwert für diese Eigenschaft bereitzustellen.“ Wie bereits erwähnt, steuert UIKit den Ertrag Viele Anpassungen und Einstellungen.

Eines der hübschen Nebenprodukte ist jedoch, wie loadView jetzt aussieht:

Überschreibe func loadView
{
    self.view.addSubview (self.helloWorldLbl)
}

Beachten Sie jedoch das "()" am Ende des Abschlusses in der Eigentumserklärung. Dadurch erfahren die kleinen Swift-Assistenten, die Ihren Code kompilieren, dass die Instanz dem Rückgabetyp des Abschlusses zugewiesen wird. Wenn wir dies weglassen, ist es möglich, dass wir der Instanz den eigentlichen Abschluss selbst zuweisen.

Und in diesem Fall ist das .

Regeln sind Regeln

Obwohl wir ein glänzendes neues Spielzeug haben, ist es unerlässlich, sich an die Regeln des Landes zu erinnern. Da wir einer Schließung eine Eigenschaft zuweisen, wurde der Rest der enthaltenen Instanz möglicherweise noch nicht initialisiert. Wenn der Abschluss ausgeführt wird, ist es aus diesem Grund nicht möglich, in ihm auf andere Eigenschaftswerte oder auf self zu verweisen.

Zum Beispiel:

let helloWorldLbl: UILabel = {
    let lbl = UILabel ()
    lbl.text = self.someFunctionToDetermineText () // Compiler-Fehler
    lbl.font = UIFont.preferredFontForTextStyle (UIFontTextStyleBody)
    lbl.textColor = self.myAppTheme.textColor () // Ein weiterer Fehler
    lbl.textAlignment = .Center
    return lbl
} ()

Die Instanz von self ist möglicherweise noch nicht sicher oder hat den zweiphasigen Initialisierungsprozess von Swift noch nicht abgeschlossen. Dies gilt auch für Instanzeigenschaften, die zugewiesen und initialisiert werden können oder nicht, da der Abschluss sofort ausgeführt wird.

Dies ist ein deutlicher, aber gerechtfertigter Nachteil bei der Verwendung von Verschlüssen für die Initialisierung. Es ist jedoch durchaus sinnvoll - und es entspricht einem der drei Designziele von Swift: Sicherheit.

Immer süßer mit Sammlungen

Ein Bereich, in dem ich diese Technik besonders nützlich fand, sind Instanzen, die eine der vielen verschiedenen Formen einer Sammlung in Swift darstellen. Von den vielen Talenten von Swift gehört das Durchsuchen und Durchsuchen von Sammlungen mit der Kraft von tausend Titanen zu meinen Favoriten.

Betrachten Sie das folgende Beispiel aus einem Initialisierer in einem Projekt, an dem ich gerade arbeite. Die Klasse, die diesen Code enthält, verfügt über eine [Developer] -Eigenschaft. Bei einem Neustart habe ich ihre Anfangswerte aus einer .plist-Datei festgelegt. Anschließend werden diese über NSKeyedArchiver gespeichert.

Wache lassen devs = NSKeyedUnarchiver.unarchiveObjectWithFile (DevDataManager.ArchiveURL.path!) als? [Entwickler] sonst
{
    self.developers = {
        let pListData = // Plist-Daten abrufen
        var devArray: [Developer] = [Developer] ()
        // devArray aus Plist-Daten einrichten
        return devArray.map {$ 0.setLocation ()}
                       .filter {$ 0.isRentable}
                       .sort {$ 0.name <$ 1.name}
     } ()
    Rückkehr
}
self.developers = devs

Mir gefällt dieser Ansatz sehr gut, denn obwohl wir ihn nicht außerhalb eines Initialisierers verwenden, ist die Absicht des Codes sehr klar, da er nur für das Festlegen einer Eigenschaft verantwortlich ist.

Wenn die Überschreibungen von Initialisierern und viewDidLoad größer werden, ist das (zumindest) Herausschneiden solcher Elemente ein willkommenes Geschenk für die Lesbarkeit.

NSCute bekommen

Wenn Sie die Initialisierung von Dingen nur wirklich mit einem Closure durchführen, aber unter dem gravierenden Mangel leiden, diese funktionalen $ s in Ihrem Code zu verwenden, sollten Sie aufheitern. Mit etwas geschickter Swiftery kann man Code erstellen, der den Typ in den Verschluss selbst einfügt, was zu einer Pro-Style-Konfiguration führt. Betrachten Sie diesen Code, der mir zum ersten Mal auf dem immer informativen NSHipster begegnet ist:

@warn_unused_result
public func Init  (Wert: Type, @noescape block: (Objekt: Type) -> Void) -> Type
{
    Block (Objekt: Wert)
    Rückgabewert
}

Ich mag wo das hinführt. Eine öffentliche Funktion, die einen Abschluss mit einem typisierten Objekt unter Verwendung von Generika vornimmt und diesen Typ dann zurückgibt. Dies bedeutet, dass Sie sich umdrehen und Dinge mit mehr Typinformationen initialisieren können. Unser erstes Codebeispiel würde dann wiederum so aussehen:

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

Obwohl es den Anschein hat, als würde es die Notwendigkeit der Instanzvariablen aus dem Closure heraus zunichte machen, und die "()" - Anforderung wird beseitigt. Sehr schön .

Abschließende Gedanken

Man könnte sagen, dass eine solche Technik in einer Hand sechs und in der anderen ein halbes Dutzend ist. Es stimmt zwar, dass die vom Programmierer erstellten Codezeilen weitgehend gleich bleiben, ich würde jedoch argumentieren, dass sie aufgrund ihrer Platzierung und Flexibilität für viele Szenarien ideal sind.

Es ist eine unterhaltsame Art, Dinge zu erledigen, und es gibt sogar ein paar Möglichkeiten, dasselbe in unserem alten Freund Objective-C zu tun. Aber hey, je mehr du weißt, amirite?

Bis nextWeek = {let week = Week () week.advancedBy (Tage: 7)} ()

Jordan Morgan ist ein iOS-Softwareentwickler, der Dreaming In Binary ausführt

Wenn Sie über die Verwendung von Verschlüssen für Initialisierungen informiert sind, können Sie NSRecommend (hier: unten) verwenden.