VISITAS:

Mostrando entradas con la etiqueta swift. Mostrar todas las entradas
Mostrando entradas con la etiqueta swift. Mostrar todas las entradas

lunes, 24 de agosto de 2015

SWIFT (V) : Control de flujo

Sea cual sea el lenguaje de programación en el que estemos trabajando, el desarrollo de aplicaciones es siempre un ejercicio de lógica: tomar decisiones en función de ciertas condiciones y ejecutar múltiples veces cierta lógica sobre unos datos. Todo esto se suele denominar control de flujo, y swift tiene su propia sintaxis, no muy diferente por cierto a la de otros lenguajes.

Sentencia for

Swift ofrece dos tipos de for: condition-increment y for-in

Condition-increment for

La sintaxis es similar (aunque no idéntica) a otros lenguajes C-like:

    for var i = 0; i < 15; i++ {
        println(i)
    }

For-in for

Esta estructura de control permite iterar por los datos de una colección (las colecciones se explican más adelante) o por un rango numérico.
La sintaxis es la siguiente:

    for nombre in colección_o_rango {
        ....
    }

Por ejemplo:

    for i in 1...5 {
        println(i)
    }

Este tipo de for también sirve para iterar por los caracteres de un string:

    for c in "Esto es un ejemplo" {
    }

La variable no es obligatorio declararla. Esto puede ser útil cuando, por ejemplo, sólo se quieren contar los elementos de la colección. En este caso se pone el carácter _:

    for  _  in "Esto es una cadena" {
        counter++
    }

Sentencia while

El bucle for se utiliza cuando sabemos de antemano el número de repeticiones. Pero en otras ocasiones necesitamos repetir hasta que se cumpla una determinada condición y no sabemos de antemano el número de repeticiones que habrá que hacer.

Sintaxis de while:

    while condición {
    }

La condición tiene que se de tipo Bool, y se ejecuta hasta que sea false.

Sentencia do .. while

Este bucle es similar al bucle while, excepto que siempre se ejecuta al menos una vez.

    do {
    } while condición

Salir de un bucle: break

A veces es necesario salir del bucle cuando en su interior se detecta alguna condición que obliga a salir. Para ello se utiliza la sentencia break:

    while a > 10 {
        ....
        if otra_condicion {
            break
        }
        ....
    }

Sentencia continue

La sentencia continue hace que volvamos al principio del bucle, saltando todo lo que queda. Suele ser útil para no hacer nada cuando se cumple una determinada condición.

    while a > 10 {
        if personas[a] == "001" {
            continue
        }
        .....
    }

Sentencia if

La sentencia if (y todas sus variantes) es muy similar a la de otros lenguajes de programación:

    if condición_1 {
        .....
    } else if condición_2 {
        .....
    } else if condición_3 {
        .....
    } else {
        .....
    }

Sentencia switch

La sentencia switch es equivalente a if/else if/else, pero se utiliza cuando el número de casos a comprobar es grande:

    switch expresión {
        case valor1:
            .....  // no se pone break
        case valor2:
            .....  // no se pone break
        default:
            ....
    }

Se pueden combinar varios valores:

    switch expresión {
        case valor1, valor2, valor3:
            ..... 
        case valor4, valor5:
            ..... 
        default:
            ....
    }

También se pueden poner rangos:

    switch expresión {
        case valor1 ... valor2:
            ..... 
        default:
            ....
    }

Algo muy novedoso de la sentencia switch en swift es el uso de la cláusula where, que añade una condición adicional a evaluar:

    switch expresión {
        case valor1 where condición:
            ..... 
        default:
            ....
    }




SWIFT (VIII) : Arrays y Diccionarios

Los arrays y los diccionarios de swift son colecciones de otros objetos y son muy similares a los que ya existen en otros lenguajes de programación. La diferencia entre ambos es el modo de acceso a los objetos que contienen: en los arrays se suele acceder por índice y en los diccionarios se suele acceder por clave. Los diccionarios en otros lenguajes de programación se denominan mapas.

Colecciones mutables e inmutables

Antes de empezar a ver tanto arrays como diccionarios, es preciso indicar que estas colecciones en swift pueden ser mutables o inmutables. Una colección inmutable no se puede modificar una vez que se ha inicializado. Para hacer una colección inmutable, la asignamos a una constante (let constante = colección). Por otro lado, las colecciones mutables sí se pueden modificar después de haberse inicializado. Para hacer una colección mutable, se asigna a una variable (var variable = colección).

Arrays

Un array es una colección que guarda múltiples objetos de forma ordenada. Todos los valores que se guardan en un array tienen que ser del mismo tipo.

Inicialización de arrays

Un array puede inicializarse en la creación mediante una colección de valores (array literal):

    var array : [type] = [ value1, value2, value3 ]

Por ejemplo:

    var cadenas : [String] = [ "Hola", "Mundo" ]

Igualmente, podemos crear un array inmutable:

    let plantas : [String] = [ "helecho", "tomillo" ]

No es obligatorio declarar el tipo del array, se puede dejar que el compilador lo infiera (tanto en constantes como en variables):

    let plantas = [ "helecho", "tomillo" ]

También podemos crear un array vacío:

    var lista = [String]()

Existe una forma de crear un array con una serie de elementos repetidos:

    var nombres = [String](count: 12, repeatedValue: "nombre")

Por último, se puede crear un array con la concatenación de otros arrays:

    var lista = lista1 + lista2

Trabajando con arrays

Tamaño de un array:

    array.count

Comprobar si un array está vacío:

    array,isEmpty

Acceder (en lectura y en escritura) a los items de un array (el primer elemento es el índice 0):

    var a = array[3]
    array[3] = 4

Añadir items a un array

Se pueden añadir elementos a un array mediante la función append o mediante el operador +. Los nuevos items se añaden al final del array, aumentando su tamaño:

    array.append("Hola")
    array += [ "Pepe", "Mundo" ]

También se pueden insertar elementos en una posición concreta de un array, desplazando los demás elementos hacia la derecha:

    array.insert("Manzano", atIndex: 3)

Eliminar items de un array

Se puede eliminar un elemento de una posición concreta de un array, desplazando los demás elementos hacia la izquierda:

    array.removeAtIndex(2)

También se puede eliminar el último elemento de un array:

    array.removeLast()

Iterar por un array

Se puede iterar por un array mediante for:

    for arbol in listaArboles {
        println(arbol)
    }

Diccionarios

Los diccionarios en swift, al igual que en otros lenguajes de programación, permiten almacenar datos en forma de parejas clave-valor. Las claves son únicas y permiten acceder a los valores correspondientes. Los diccionarios son colecciones no ordenadas.
Actualmente, swift sólo permite que las claves sean de tipo String, Int, Double y Bool (supongo que más adelante se ampliarán estos tipos utilizando técnicas similares a las de Java).

Inicialización de diccionarios

Forma de crear e inicializar un diccionario:

    var diccionario : [tipo-key: tipo-valor] = [ key1: value1, key2 : value2 ]

Por ejemplo:

    var personas = [ "001":"José Luis", "005":"Pedro" ]

En este caso se podría haber especificado el tipo, pero se puede dejar que el compilador lo infiera:

    var personas : [String: String] = [ "001":"José Luis", "005":"Pedro" ]

Creación de un diccionario vacío:

    var personas = [String: String]()

Trabajando con diccionarios

Número de elementos en un diccionario:

    personas.count

Acceso a un elemento:

    personas["001"]

Escritura:

    personas["001"] = "Samuel"

Añadir elementos a un diccionario

Se puede añadir un elemento a un diccionario directamente, por su clave:

    personas["045"] = "John"

Eliminar elementos de un diccionario

Para eliminar un elemento de un diccionario, ponemos su valor a nil:

    personas["001"] = nil

Iterar por un diccionario

Al igual que ocurría con los arrays, se puede iterar por todos los elementos de un diccionario utilizando un bucle for:

    for (id, nombre) in personas {
        println("Id = \(id) es \(nombre)")
    }






jueves, 20 de agosto de 2015

SWIFT (IV) Operadores y expresiones

Swift tiene prácticamente los mismos operadores que casi todos los lenguajes C-like (C, C++, Java, Objective-C, Javascript, etc), así que este artículo no será muy novedoso para los programadores experimentados en alguno de estos lenguajes.

Operador de asignación

El operador de asignación simplemente asigna el resultado de una expresión (a la derecha) a una variable (a la izquierda):

    var x : Int?
    x = 10 + 45
    x = x! + 1

Operadores aritméticos

    -  si es unario cambiar el signo, si es binario resta
    * multiplicación
    / división
    + suma
    % módulo o resto

Operador de asignación compuesta

    x += 3

Es equivalente a:

    x = x + 3

Operadores de incremento y decremento

    x++;

Es equivalente a:

    x = x + 1

Y también:

    x--

Es equivalente a:

    x = x - 1

Operadores de comparación

Igualdad:    ==
Desigualdad:   !=
Comparaciones numéricas:   <, >, <=, >=

Operadores lógicos booleanos

NOT ! invierte el valor de un booleano
OR || retorna true si uno de sus operandos es true
AND && retorna true si sus dos operandos son true
XOR ^ retorna true si sus dos operandos son distintos (true, false o false,true)

Operadores de rango

Los operadores de rango se suelen emplear en bucles.
La sintaxis del operador de rango básico es:

    a ... b

Esto representa el rango de valores entre a y b, ambos incluidos. Por ejemplo, 3 ... 6 representa los números 3, 4, 5 y 6.
Si se desea no incluir el último número:

    a ..< b

En este caso, el número b no va incluido en el rango.

Operador ternario

Al igual que en muchos lenguajes C-like en swift existe también el operador ternario:

    condición ? expresión_true : expresión_false

El resultado de esta expresión depende del valor de la condición. Si la condición es true, entonces el resultado de la expresión será la expresión_true. Si la condición es false, entonces el resultado de la expresión será la expresión_false.

Operadores de bit

Operador unario NOT de bit, cambia los bits a 1 por 0 y los bits 0 por 1:

    var x : UInt8 = 3      // 00000011
    var y : UInt8 = ~x    // 11111100       

Operador binario AND de bit, realiza un AND a nivel de bit de los dos operandos:

    var x : UInt8 = 12        // 00001100
    var y : UInt8 = 5          // 00000101
    var z : UInt8 = x & y   // 00000100

Operador binario OR de bit, realiza un OR a nivel de bit de los dos operandos:

    var x : UInt8 = 12        // 00001100
    var y : UInt8 = 5          // 00000101
    var z : UInt8 = x | y     // 00001101

Operador binario XOR de bit, realiza un XOR a nivel de bit de los dos operandos:

    var x : UInt8 = 12        // 00001100
    var y : UInt8 = 5          // 00000101
    var z : UInt8 = x ^ y    // 00001001

Desplazamiento de bits a la izquierda, metiendo ceros por la derecha:

    var x : UInt8 = 12       // 00001100
    var z : UInt8 = x << 1 // 00011000

Desplazamiento de bits a la derecha, metiendo ceros por la izquierda:

    var x : UInt8 = 12       // 00001100
    var z : UInt8 = x >> 1 // 00000110

    

martes, 18 de agosto de 2015

SWIFT (VII) : Clases y Objetos

Swift es un lenguaje orientado a objetos para desarrollar aplicaciones iOS y OSX.
No es el objetivo de este capítulo explicar los conceptos de orientación a objetos (para eso existen multitud de libros y artículos), sino explicar cómo estos conceptos se implementan con swift.

Clases

Una clase se define de la siguiente forma:

class NombreDeLaClase : ClasePadre {
    // propiedades
    // métodos de instancia
    // metodos de clase
}

Las propiedades son variables y constantes. Se declaran de la misma forma que las variables y constantes en swift, es decir, con var y let.
Los métodos de instancia son funciones que se invocarán a través de un objeto de la clase. Se declaran de forma similar a las funciones de swift, es decir, con func.
Los métodos de clase son comunes a toda la clase y no se necesita un objeto para invocarlos (son similares a los métodos estáticos de otros lenguajes). Se declaran como funciones normales, pero con class func.

Veamos un ejemplo:

class GraphicObject {
    var x : Float = 0
    var y : Float = 0
    func show() {
        println("x=\(x), y=\(y)")
    }
    class func getMaxWidth() -> Float {
        return 240.0
    }
}

Clase derivada:

class Rectangle : GraphicObject {
    var width : Float = 0
    var height : Float = 0
}

Objetos

Declaración de un objeto:

var obj : Rectangle = Rectangle()

Constructores

En swift, una forma más correcta de llamar a los constructores sería: inicializadores.
Declaración del constructor (nótese que no se pone func en el constructor):

class Rectangle : GraphicObject {
    var width : Float = 0
    var height : Float = 0
    init( w : Float, h : Float ) {
        width = w
        height = h
    }
}

Cuando se instancia el objeto hay que pasar los argumentos que necesita el constructor (si no se pasan los argumentos adecuados el compilador da un error, además, los argumentos deben pasarse nombrados obligatoriamente):

var obj = Rectangle( w: 20.0, h: 10.0 )

Como se puede ver, los constructores no son funciones normales: no empiezan con func; hay que nombrar los argumentos obligatoriamente en la llamada; tienen un nombre especial (init).

Deinicializadores

Un deinicializador (destructor en otros lenguajes) se invoca justo antes de que el objeto se destruya. La destrucción del objeto ocurre cuando el recolector de basura así lo decide.

La sintaxis del deinicializador es especial, aunque es similar a la de un inicializador sin parámetros:

class Rectangle : GraphicObject {
    var width : Float = 0
    var height : Float = 0
    init( w : Float, h : Float ) {
        width = w
        height = h
    }
    deinit {
        // labores de limpieza del objeto
        println("cleanup")
    }
}

Llamada a métodos y acceso a propiedades

El acceso a las propiedades y métodos de un objeto es muy sencillo en swift (similar a otros lenguajes):

var a = obj.width
obj.width = 70.0
obj.show()

Para llamar a un método de clase:

Rectangle.getMaxWidth()

Propiedades almacenadas y calculadas

Las propiedades pueden ser de uno de los siguientes tipos: almacenadas o calculadas.
Las propiedades almacenadas son las que hemos visto hasta ahora, contienen su valor en una variable o una constante.
Una propiedad calculada es el resultado de un cálculo o una lógica. Para implementar variables calculadas tenemos que declarar un método getter y (opcionalmente) un método setter, para leer y escribir el valor de la variable respectivamente. La variable en sí misma podría incluso no existir.

class Rectangle : GraphicObject {
    .......
    var area : Float {
        get {
            return w * h
        }
    }
}

Se puede declarar igualmente el setter con la palabra reservada set.

class Rectangle : GraphicObject {
    .......
    var widthInInches : Float {
        get {
            return width / 2.54
        }
        set(inches) {
            width = inches * 2.54
        }
    }
}

Self

En swift, al igual que en otros muchos lenguajes orientados a objetos, desde dentro de los métodos de instancia se puede acceder al propio objeto. Para ello, se utiliza self (similar a this en otros lenguajes). No es obligatorio utilizar self, pero sí es recomendable para aclarar. Aunque en algunos casos sí es obligatorio, por ejemplo cuando algún nombre de parámetro de función coincido con el nombre de una propiedad:

    func setValues(width : Float, height : Float) {
        self.width = width
        slef.height = height
    }

Sobreescribir métodos

La herencia de clases supone que la clase hija hereda todas las propiedades y métodos de la clase padre, pero además, la clase hija puede añadir nuevas propiedades y métodos, ampliando así la funcionalidad de la clase padre. Pero otra manera de ampliar (o especializar) la funcionalidad de la clase padre es sobreescribiendo métodos en la clase hija:

override func show() {
    println("width=\(width), height=\(height)")
}

Desde un método sobreescrito se puede invocar al método original de la clase padre de la siguiente forma:

override func show() {
    super.show()
    println("width=\(width), height=\(height)")
}

Inicialización de subclases

La inicialización de un objeto se realiza en el inicializador de la clase, como ya hemos visto. En el caso de subclases, la clase hija es responsable de llamar al inicializador de la clase padre:

init( x: Float, y : Float, w : Float, h : Float ) {
    width = w
    height = h
    super.init(x : x, y : y)
}

La llamada al constructor de la clase padre se puede realizar en cualquier punto del inicializador de la clase hija (en algunos lenguajes se obliga a llamarlo al principio).




lunes, 17 de agosto de 2015

SWIFT (I) Tipos de datos, constantes y variables

Tipos de datos

Todas las constantes y variables swift tienen un tipo de datos. El programador puede especificar el tipo de datos de una constante o variable, o bien puede dejar que sea el compilador el que decida el tipo en función del valor que se asigna inicialmente a la constante o variable.

Enteros

Los enteros pueden almacenar números positivos o negativos sin decimales.
Swift proporciona los siguientes tipos enteros: con signo (Int8, Int16, Int32, Int64) y sin signo (UInt8, UInt16, UInt32, UInt64).También proporciona el tipo Int que el compilador asignará al tipo más adecuado para la plataforma. Apple recomienda utilizar el tipo Int, salvo que se desee una longitud específica.
Todos los tipos tienen las propiedades min y max para obtener los valores mínimo y máximo respectivamente de cada tipo.

Punto flotante

Los números en punto flotante pueden tener dígitos decimales.
Swift proporciona dos tipos de punto flotante: Float (32 bits, precisión 6 decimales) y Double (64 bits, precisión 15 decimales).

Boolean

Swift proporciona el tipo Bool con las constantes true y false para definir el tipos booleano.

Carácter

El tipo Character para almacenar un único carácter. Las variables carácter pueden tener un carácter imprimible como "A" o bien un carácter UNICODE: "\u{0058}"
Existen algunos caracteres especiales:

    var newline = "\n"
    var backslash = "\\"
    var doublequote = "\""
    var singlequote = "\'"

String

Un String es una cadena de caracteres.
Los String en swift se pueden construir utilizando "string interpolation":

    var message = "Juan tiene \(edad) años"

Variables

Las variables en swift se declaran mediante la palabra reservada var y se puede añadir opcionalmente el tipo de datos:

    var contador = 7
    var otroContador : Int = 67

Constantes

Una constante es similar a una variable, pero no se puede modificar.
Al igual que las variables, las contantes se declaran mediante la palabra reservada let y se puede añadir opcionalmente el tipo de datos:

    let maximo = 6757
    let nombre : String = "Valor"

Apple recomienda utilizar constantes siempre que sea posible, ya que su rendimiento es superior ligeramente al de las variables.

Tuplas

Las tuplas en swift permiten almacenar varios valores en una única entidad. Los items de una tupla pueden ser de cualquier tipo (dentro de una misma tupla, cada valor puede ser de un tipo diferente).
Por ejemplo:

    let tupla = ( 10, 4.56, "Nombre" )

Se accede a sus componentes por índice (empezando en cero):

    tupla.1  --> 4.56

Se pueden crear tuplas asignando un nombre a cada valor de la tupla:

    let tupla = ( contador : 7, media : 47.5, nombre : "Nombre" )

    tupla.media --> 47.5

Las tuplas son muy útiles para retornar múltiples valores desde una función.






SWIFT & iOS en Español


Swift

  1. Swift: Tipos de datos, constantes y variables
  2. Swift: Tipos optional
  3. Swift: Type cast y chequeo de tipos
  4. Swift: Operadores y expresiones
  5. Swift: Control de flujo
  6. Swift: Funciones y closures
  7. Swift: Clases y objetos
  8. Swift: Arrays y diccionarios

iOS

martes, 11 de agosto de 2015

SWIFT (VI) FUNCIONES Y CLOSURES

Las funciones son algo típico en todos los lenguajes de programación clásicos. Los closures ya no lo son tanto, al menos, no son tan conocidos, aunque llevan existiendo muchos años en lenguajes tan conocidos como Javascript.

FUNCIONES EN SWIFT

Una función es un bloque de código que puede ser invocado por su nombre para ejecutar una determinada tarea. Puede recibir datos de entrada y puede retornar uno o más resultados. Cuando se define una función se especifican los parámetros de entrada y el tipo del valor retornado (si los hay). Cuando se invoca a una función se pasan datos como argumentos a la función y se puede recoger el resultado en una variable, por ejemplo. Nótese la sutil diferencia entre parámetros y argumentos.

Ejemplo de declaración de una función en swift:

    func suma(operando1 : Int, operando2 : Int) -> Int {
        return operando1 + operando2
    }

Llamada a una función:

    var a : Int = suma(3, 6)

Parámetros externos

En el ejemplo anterior, los nombres de los parámetros son internos a la función y no pueden utilizarse en la llamada, es decir, la llamada se basa únicamente en el orden de los parámetros. Pero a veces es mejor (o más claro) que en la llamada a la función se especifique también el nombre de los parámetros. Para ello se antepone el carácter # al nombre del parámetro:

    func resta(#minuendo : Int, #sustraendo : Int) -> Int {
        return minuendo - sustraendo
    }

Y la llamada:

    var resultado : Int = resta( minuendo : 5, sustraendo : 3)

Parámetros por defecto

Swift permite especificar un valor por defecto para un parámetro (o parámetros) que pudieran no especificarse en la llamada a función. Estos parámetros con valores por defecto deben situarse al final de la lista de parámetros en la declaración de la función. Cuando en un parámetro se especifica un valor por defecto, swift asume que ese parámetro es externo y permite su nombrado en la llamada (ver subapartado anterior "parámetros externos").

    func info(account : Int, owner : String  = "Unknown") -> String {
        return "Account: \(account), owner: \(owner)."
    }

Ejemplos de posibles llamadas a esta función:

    var s1 = info(26351)  --> Account 26351, owner: Unknown.
    var s2 = info(10005, owner : "John")  --> Account 10005, owner: John.

Retornando múltiples valores desde una función

Una función puede retornar múltiples valores de varias formas: retornando un objeto, retornando un array o diccionario, y por último, retornando una tupla. Veamos este último caso.

    func getUserInfo(id : String) -> (nombre : String, apellido : String, dni : Int) {
        // buscar información del usuario en BD por ejemplo
        return ("Juan", "López", "4747563-P")
    }

Llamada a esta función:

    var info = getUserInfo("A3625")
    println("\(info,nombre) \(info,apellido) - \(info.dni)")

Número variable de parámetros

En swift una función puede recibir un número variable de parámetros (puede ser cero). Esto se indica con ...
Dentro de la función los parámetros son un array.

    func printInfo(datos : String...) {
        for s in datos {
            println(s)
        }
    }

Llamada:

    printInfo("a", "b", "c", "d")

Parámetros variables

Los parámetros recibidos por una función son constantes (es como si tuvieran un let delante) y no se pueden modificar dentro de la función.
Si queremos modificar alguno de los parámetros de una función, debemos declararlo variable poniéndole var delante.

    func suma(var operando1 : Int, operando2 : Int) -> Int {
        operando1 += operando2
        return operando1
    }

Esto no significa que el argumento pasado a la función se vaya a modificar, simplemente que podemos cambiar el valor internamente en la función. Para que se modifique el argumento externo, tendremos que utilizar parámetros de entrada/salida, como se describe en el siguiente subapartado.

Parámetros de entrada/salida

Para que una función pueda modificar el valor de un argumento es necesario definir el parámetro correspondiente como inout:

    func duplica(inout valor : Int) {
        valor = 2 * valor
    }

En la llamada tenemos que anteponer el carácter & al argumento (el cual tiene que ser una variable obligatoriamente):

    var v : Int = 4;
    duplica(&v)
    println(v)   --> 8

CLOSURES EN SWIFT

Antes de entrar en las closures, introduzcamos el tema 

Las funciones como tipos de datos

Una característica muy interesante de swift (y de otros lenguajes de programación) es que las funciones pueden ser tratadas como tipos de datos. De esta forma, se puede por ejemplo definir una función y asignarla a una constante o variable:

    func kilometers2Miles(kilometers : Float) -> Float {
        return meters * 0.62137
    }
    let f = kilometers2Miles

Habiendo hecho esa asignación podemos invocar a la función a través de la constante:

    var result = f(100.0)

Lo interesante de esto es que cuando asignamos una función a una constante tenemos todas las capacidades de los tipos de datos. Concretamente, una función puede pasarse como argumento a otra función, o incluso ser retornada de una función.

El tipo de datos de la función lo definen los parámetros y el valor retornado. En el ejemplo anterior, la función recibe un parámetro Float y devuelve un valor Float, por tanto el tipo de esta función sería:

    (Float) -> Float

Por ejemplo, podemos definir una función que reciba otra función como parámetro:

    func invoke( theFunction : (Float) -> Float, value : Float) -> Float {
        return theFunction(value)
    }

Y podemos invocarla así:

    invoke(kilometers2Miles, 34.8)

Igualmente, podemos retornar una función:

    func getFunction() -> (Float) -> Float {
        return kilometers2Miles
    }

Expresiones closure

Una expresión closure es sencillamente un bloque de código. Las expresiones closure se pueden asignar a constantes o variables:

    let f = { println("Hola") }
    f()   --> Hola

Las expresiones closure también pueden recibir parámetros y devolver resultados. Por ejemplo:

    let m = { ( operando1 : Float, operando2 : Float) -> Float in return operando1 * operando2 }
    let v = m(20, 34.2)

La sintaxis es parecida a la de una función, pero no tiene nombre, todo va entre llaves y se utiliza la palabra reservada in para indicar el comienzo del código. De hecho, las funciones swift son expresiones closure con nombre.

Closures

Un closure es un bloque de código (una función o una expresión closure) junto con una o más variables que existen en el contexto de ese bloque de código. Por ejemplo:

    func externa() -> () -> Int {
        var contador = 0
        func interna() -> Int {
            contador += 1
            return contador
        }
        return interna
    }

    let miClosure = externa()
    let resultado = miClosure()

En este ejemplo, la función externa retorna una función interna. Realmente retorna un closure, ya que retorna la función interna más la variable contador, que está declarada en el scope de la función interna. De hecho, cada vez que se llama a miClosure() se va incrementando el contador.












domingo, 9 de agosto de 2015

SWIFT (III) TYPE CAST Y CHEQUEO DE TIPOS

TYPE CASTING

Hay dos tipos de conversión de tipos en swift:
  • upcasting: se trata de convertir un objeto de una clase a otra clase padre (es decir, subir hacia arriba en la jerarquía)
  • downcasting: se trata de convertir un objeto de una clase a otra clase derivada (es decir, bajar en la jerarquía)
El upcasting es siempre seguro (ya que siempre podemos convertir un objeto de una clase a una de sus clases padre) y el compilador lo deja hacer sin problemas. Ejemplo de upcasting:

        var b : UIButton = UIButton()
        var c : UIControl = b as UIControl

En este ejemplo, como UIControl es una clase padre de UIButton, podemos hacer tranquilamente el casting (upcasting) de UIButton a UIControl.

Sin embargo, el downcasting es responsabilidad del programador, es decir, el compilador no puede asegurarlo, y por tanto, el lenguaje swift tiene una syntaxis especial para el downcasting. Cuando se hace downcasting utilizando as, el compilador da un error avisando que no puede convertir el tipo:

        var c : UIControl = UIButton()
        var b : UIButton = c as UIButton    --> error de compilación

La primera sentencia es correcta, sin embargo, la segunda dará un error de compilación advirtiendo que no puede convertir los tipos de forma segura. Ese error no significa que se esté haciendo algo incorrecto, sólo significa que el compilador no puede asegurar la conversión.

El downcasting en swift se hace utilizando un casting forzado con as!

        var c : UIControl = UIButton()
        var b : UIButton = c as! UIButton   --> correcto

En este caso se deja al programador la responsabilidad de que c sea de la clase UIButton o una de sus clases derivadas. Si no fuera así, se elevaría una excepción en runtime (ver más abajo cómo chequear el tipo de una variable).

Existe una tercera forma de casting en swift: optional binding. Para ello se emplea as?

En este caso, la conversión retorna un optional, y si no es posible la conversión, retornará nil. Por ejemplo:

        var b : UIButton? = c as? UIButton   --> si c no puede convertirse a UIButton, entonces el resultado es nil, lo cual es válido porque la variable b es optional

Igualmente podemos emplear esta técnica en un if como se hace con los tipos optional:

        if let b = c as? UIButton {
                b puede utilizarse como un UIButton
        }

TYPE CHECKING

Además del casting, en swift es posible chequear el tipo de una variable:

        if b is UIButton {
                // b es una instancia de UIButton y por tanto podemos utilizarlo como tal
                c = b as! UIButton   --> esto va a funcionar
        }

Combinando todas estas técnicas podemos manipular el tipo de las variables dentro de su jerarquía.













SWIFT (II) TIPOS OPTIONAL

Swift es un lenguaje de programación moderno que utiliza las mejores técnicas de los lenguajes de programación que han tenido más éxito en los últimos años: Java, Objective-C, Python, etc.
Este nuevo lenguaje de programación "inventado" por Apple para que sea su lenguaje del futuro en plataformas iOS y OSX tiene muchísimas ventajas, porque ha cogido lo mejor de cada lenguaje, convirtiéndose hoy en día (2015) en el lenguaje más seguro y potente que existe.
Sin embargo, hay ciertas características de swift que no han sido "copiadas" de otros lenguajes y que suponen una barrera de entrada al lenguaje, incluso para programadores muy experimentados. Una de esas barreras son los tipos optional, algo que bloquea a muchos programadores cuando empiezan con swift (conozco a algún programador que empezó con mucha ilusión a aprender swift y cuando se encontró con los tipos optional, después de dar muchas vueltas al tema, no consiguió entenderlo y abandonó el lenguaje "para otro momento").
Voy a intentar explicar (en español y con ejemplos) los tipos optional de swift para que no sean una barrera para nadie. Supongo que se me quedarán algunas cosas en el tintero, y también supongo que cuando el lector pase por este blog, el lenguaje swift habrá cambiado bastante y que algunas de las cosas que aquí expongo podrían haber cambiado (prometo mantener esta entrada al día en la medida de lo posible). 

TIPOS OPTIONAL

Los lenguajes de programación permiten expresar la situación en la que una variable no contiene ningún valor. Esto es muy útil, por ejemplo, cuando queremos retornar nil (o null) desde una función indicando que no se retorna nada. También es útil para indicar que una variable todavía no se ha inicializado. En lenguajes como C++ o Java, para indicar que una variable no contiene ningún valor, se asigna el valor nil a la variable. Esta solución funciona para variables de tipo objeto, pero no para los tipos básicos: int, double, boolean, etc. En estos casos teníamos que asignar valores especiales para indicar que la variable no tiene valor, por ejemplo -1. Swift resuelve este problema de manera general para todos los tipos mediante los tipos optional.

En Swift se definen los tipos optional añadiendo el carácter ? al final del tipo. Ejemplos:

        Int?
        String?
        MiClase?
        etc

En Swift, una variable de un tipo optional puede contener un valor (básico u objeto, según el tipo) o bien no contener ningún valor. Sólo se puede asignar nil a una variable de un tipo optional. A una variable que no sea optional (sin el carácter ?), no se le puede asignar nil, ya que el compilador daría error:

        var i : Int? = nil
        var j : Int = nil   --> error de compilación

Igualmente si una variable es de tipo optional, no podemos asignarla a otra variable que no sea optional (incluso aunque contenga un valor).

        var k : Int? = 7
        var m : Int = k   --> error de compilación

Tenemos que pensar que los tipos Int e Int? no son lo mismo. El tipo Int? es un wrapper de Int, es decir, una especie de objeto que contiene un Int, por tanto, no podemos asignar un Int? a un Int. Sin embargo, sí es posible asignar un Int a un Int?, ya que el compilador hace las comversiones adecuadas. En el siguiente apartado veremos cómo asignar una variable Int? a otra de tipo Int.

En resumen, cualquier tipo Swift puede ser optional, tanto los objetos como los tipos básicos. Y sólo podremos asignar nil a una variable de un tipo optional.

FORCED UNWRAPPING

Forced unwrapping es el mecanismo que proporciona swift para asignar una variable de un tipo optional a una variable no optional. Forced unwrapping permite obtener la variable interna (si existe) que tiene el wrapper optional. Por ejemplo:

        var a : Int? = 5
        var b : Int = a!

Añadiendo el carácter ! al final de la variable se hace un forced unwrapping de la variable optional, es decir, se extrae el valor de la variable si existe (si la variable no contuviera un valor, o sea nil, se produciría una excepción en runtime).

Cuando se va a hacer forced unwrapping Para comprobar si el valor de una variable es conveniente comprobar primero si la variable optional es distinta de nil, es decir, si tiene un valor. Para ello se compara utilizando los operadores == y != (habituales en lenguajes de programación tipo C). Si hemos comprobado que una variable es != nil porque hemos hecho un if previamente, entonces podríamos leer su valor con tranquilidad:

        if a != nil {
                b = a!
                print("Se puede leer el valor de a = \(a!) sin problemas")
        } else {
                print("La variable a no contiene ningún valor")
        }

OPTIONAL BINDING

Optional binding es una técnica de swift que permite, al mismo tiempo que se comprueba que una variable optional tiene un valor, asignar ese valor a otra variable (o constante) para utilizarla después sin necesidad de forced unwrapping.

La sintaxis del optional binding es:

        var a : Int? = 4
        if let b = a {
                print("Se puede leer el valor de b = \(b) sin problemas")
        } else {
                print("La variable a no contiene ningún valor")
        }

Se pueden utilizar constantes (let) o variables (var) en optional binding, dependiendo de si el valor se va a modificar después o no.

También se pueden crear varias variables y constantes en un optional binding:

        if let a1 = a, b1 = b {

IMPLICIT UNWRAPPED OPTIONALS

Algunas veces, tenemos claro que una variable optional va a tener siempre un valor (es decir, el programador sabe que esa variable siempre va a contener un valor). En estos casos puede ser útil indicar en la declaración este hecho. Para ello, se utiliza la siguiente sintaxis:

        var i : Int!  --> se inicializa a nil
        i = 5

Este tipo de optionals se llaman implicit unwrapped optionals, indicando que la variable es optional pero el compilador va a hacer un unwrapped de forma implícita cada vez que se utilice. Por ejemplo:

        var j = i   --> no hace falta forced unwrapping

Lo primero que nos preguntamos tras leer estas líneas es: Y entonces, ¿por qué no declaramos la variable como no optional? Es la pregunta obvia después de la definición de implicit unwrapped optional. Veamos un caso útil en un constructor de una clase:

        class Ciudad {
                var nombre : String!    --> se inicializa a nil, por tanto tiene que ser optional
                init( nombreCiudad:String) {
                        self.nombre = nombreCiudad   --> se le asigna un valor y ya nunca va a ser nil
                }
        }

A partir de ese momento, la variable city.nombre se puede utilizar normalmente, sin hacer forced unwrapping. Es como si dejáramos en la responsabilidad del programador asegurar que una variable nunca va a ser nil (aunque para el compilador, sí podemos asignarle un valor nil).

Alguien podría decir ahora: "Yo tengo una solución más sencilla a este caso"

        class Ciudad {
                var nombre : String = ""  --> no tiene porqué ser optional
                init( nombreCiudad:String) {
                        self.nombre = nombreCiudad
                }
        }

Y tendría razón, así resolvemos también el problema. Pero podríamos complicar el caso un poco más y la solución ya no sería tan sencilla:

        class Ciudad {
                var elAlcalde : Alcalde!    --> se inicializa a nil
                init( nombreAlcalde:String) {
                        self.elAlcalde = Alcalde(nombreAlcalde)
                }
        }

Una variable implicit unwrapped optional es realmente una variable optional (por debajo) pero se puede utilizar como una variable no optional sin necesidad de hacer unwrapping cada vez que se usa. Esta es la diferencia:

        let a : String? = "Hola"
        var a1 = a!   --> es necesario forced unwrapping

        let b : String! = "Adiós"
        var b1 = b  --> se accede normalmente, a pesar de que b es optional

Con las variables implicit unwrapped optional estamos dando permiso al compilador para que haga un unwrap automáticamente cada vez que se utiliza. Es nuestra responsabilidad asegurar que la variable contiene un valor (si no lo tuviera, se elevaría una excepción de runtime).

Es posible también hacer un chequeo del valor de la variable cuando no estemos seguros:

        var a : String! = "hola"
        if a != nil {
                var b = a
        }

Yo creo que las variables implicit unwrapped optional son muy similares a las variables objeto de otros lenguajes, donde es nuestra responsabilidad comprobar previamente si tienen un valor, y si no lo tienen, se eleva una excepción.

CONCLUSIÓN

Apple recomienda utilizar variables optional ? cuando no estemos seguros de que la variable siempre va a contener un valor. Si estamos seguros de que siempre va a contener un valor, usemos implicit unwrapped optional !. Y si se inicializa con un valor y siempre va a tenerlo, entonces usaremos variables no optional. Éste sería un resumen final de todo este capítulo dedicado al novedoso tema de las variables optional en swift.

Si tenéis comentarios, sugerencias, habéis visto errores, por favor, no dudéis en poner un comentario en esta entrada de blog.