El miedo se instaló lentamente cuando me di cuenta de que estaba empezando a quedarme sin estas fotos de archivo

Swift + Inicialización con cierres

Cierres F.T.W.

Una nota rápida: todas mis publicaciones futuras se publicarán en mi sitio web dedicado y esta publicación ya no se actualiza. ¡Gracias por leer!

Estoy empezando a cavar realmente todo el baile de inicialización en Swift. Escribí sobre eso. Escribí sobre por qué incluso funciona como lo hace. Hice una charla sobre eso. Lo leí (mucho). Pero bueno, estoy de vuelta para un acto más sobre el asunto.

De todas las muchas, hermosas y variadas formas en que uno puede inicializar algo en Swift, el uso de cierres no suele presentarse como un método para hacerlo. Pero, por desgracia, puede hacer que el código boilerplatey ™ init () sea mucho menos doloroso y un poco más manejable.

Para los desarrolladores de interfaz de usuario programática, estos son para usted !

UIKit == UIHugeSetupCode ()

Mira, no es culpa de UIKits. Los componentes con los que un usuario debe interactuar se prestan a una montaña de código de configuración, debido a sus preferencias. Por lo general, mucho de esto se encuentra en viewDidLoad o loadView:

anular func loadView ()
{
    let helloWorldLbl = UILabel ()
    helloWorldLbl.text = NSLocalizedString ("controller.topLbl.helloWorld", comentario: "¡Hola Mundo!")
    helloWorldLbl.font = UIFont.preferredFontForTextStyle (UIFontTextStyleBody)
    helloWorldLbl.textColor = UIColor.whiteColor ()
    helloWorldLbl.textAlignment = .Center
    self.view.addSubview (helloWorldLbl)
}

Esto es bastante estándar para aquellos de nosotros que aventuramos las aguas de Cocoa Touch sin un .xib o .storyboard a la vista. Sin embargo, si comparte mi amor por los métodos minúsculos viewDidLoad o loadView, puede posponer esto en otro lugar.

Digamos, una propiedad:

let helloWorldLbl: UILabel = {
    let lbl = UILabel ()
    lbl.text = NSLocalizedString ("controller.topLbl.helloWorld", comentario: "¡Hola Mundo!")
    lbl.font = UIFont.preferredFontForTextStyle (UIFontTextStyleBody)
    lbl.textColor = UIColor.whiteColor ()
    lbl.textAlignment = .Center
    volver lbl
} ()

Bonita. En el propio libro de Apple sobre Swift, señala que "si el valor predeterminado de su propiedad requiere cierta personalización o configuración, puede usar un cierre o una función global para proporcionar un valor predeterminado personalizado para esa propiedad". Como acabamos de mencionar, los controles UIKit rinden mucha personalización y configuración.

Sin embargo, uno de los subproductos bonitos es cómo se ve loadView ahora:

anular func loadView
{
    self.view.addSubview (self.helloWorldLbl)
}

Sin embargo, tome nota del "()" al final del cierre en la declaración de propiedad. Esto permite que los pequeños asistentes de Swift que compilan su código sepan que la instancia se está asignando al tipo de retorno del cierre. Si omitiéramos esto, es posible que pudiéramos haber asignado el cierre real a la instancia.

Y en este caso, eso es .

Reglas son reglas

A pesar de que tenemos un juguete nuevo y brillante, es imperativo recordar las reglas de la tierra. Dado que estamos asignando una propiedad a un cierre, el resto de su instancia que contiene podría no haberse inicializado todavía. Por eso, cuando se ejecuta el cierre, no es posible hacer referencia a otros valores de propiedad ni a uno mismo desde dentro.

Por ejemplo:

let helloWorldLbl: UILabel = {
    let lbl = UILabel ()
    lbl.text = self.someFunctionToDetermineText () // Error del compilador
    lbl.font = UIFont.preferredFontForTextStyle (UIFontTextStyleBody)
    lbl.textColor = self.myAppTheme.textColor () // Otro error
    lbl.textAlignment = .Center
    volver lbl
} ()

La instancia de self puede no ser segura de usar todavía, o puede que no haya terminado con el proceso de inicialización de dos fases de Swift. Lo mismo es cierto para cualquier propiedad de instancia, que puede o no ser asignada e inicializada ya que el cierre se ejecuta de inmediato.

Esta es una desventaja distinta, pero justificada, con el uso de cierres para la inicialización. Sin embargo, tiene mucho sentido, y está en línea con uno de los tres objetivos de diseño de Swift: seguridad.

Gettin 'Cute con colecciones

Un área en la que he encontrado esta técnica particularmente útil es con instancias que representan una de las muchas formas diferentes de una colección en Swift. De los muchos talentos de Swift, cortar y examinar colecciones con el poder de mil titanes se erige como uno de mis favoritos.

Considere el siguiente ejemplo, tomado de un inicializador en un proyecto en el que estoy trabajando actualmente. La clase que alberga este código tiene una propiedad [Developer]. En un nuevo lanzamiento, configuro sus valores iniciales de un archivo .plist. Posteriormente, estos se almacenan a través de NSKeyedArchiver.

guard let devs = NSKeyedUnarchiver.unarchiveObjectWithFile (DevDataManager.ArchiveURL.path!) como? [Desarrollador] más
{
    self.developers = {
        let pListData = // Obtener datos de plist
        var devArray: [Desarrollador] = [Desarrollador] ()
        // Configurar devArray a partir de datos plist
        devuelve devArray.map {$ 0.setLocation ()}
                       .filter {$ 0.isRentable}
                       .sort {$ 0.name <$ 1.name}
     } ()
    regreso
}
self.developers = devs

Me gusta mucho este enfoque, porque a pesar de que no lo usamos fuera de un inicializador, la intención del código es muy clara, ya que solo es responsable de establecer una propiedad.

A medida que los inicializadores y las anulaciones de viewDidLoad se hacen más grandes, seccionar cosas como esta (como mínimo) es un regalo de bienvenida en términos de legibilidad.

Conseguir NSCute

Si realmente cava la inicialización de las cosas con un cierre, pero sufre una grave falta de uso de esos $ funcionales en su código, anímate. Usando un experto Swiftery, uno puede crear un código que infiera el tipo dentro del cierre, lo que produce una configuración de estilo profesional. Considere este código, que encontré por primera vez en el NSHipster siempre informativo:

@warn_unused_result
public func Init  (valor: Type, @noescape block: (object: Type) -> Void) -> Type
{
    bloque (objeto: valor)
    valor de retorno
}

Me gusta a donde está yendo esto. Una función pública que se cierra con un objeto escrito usando genéricos, que luego devuelve ese tipo. Esto significa que podría dar la vuelta e inicializar las cosas con más información de tipo. Nuestro primer ejemplo de código, a su vez, se vería así:

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

Aunque parezca elegante, de hecho elimina la necesidad de la variable de instancia desde dentro del cierre, y elimina el requisito "()". Muy bien .

Pensamientos finales

Se podría decir que usar tal técnica es seis en una mano y media docena en la otra. Si bien es cierto que las líneas de código creadas por el programador siguen siendo en gran medida las mismas, diría que su ubicación y flexibilidad lo hacen ideal para muchos escenarios.

Es una forma divertida de hacer las cosas, e incluso hay algunas maneras de hacer lo mismo en nuestro viejo amigo Objective-C. Pero bueno, cuanto más sabes, ¿amirita?

Hasta la próxima semana = {let week = Week () week.advancedBy (days: 7)} ()

Jordan Morgan es un ingeniero de software de iOS que ejecuta Dreaming In Binary

Si aprendió sobre el uso de cierres para inicializadores, no dude en seguir adelante y recomendar NS (esto, donde: a continuación);