¿Swift supera las limitaciones de Objective-C?

Hay muchas áreas en las que Swift es superior a Objective-C.

Seguridad de la memoria

Esto se traduce en un par de características (y otras):

• tipos opcionales (con la ayuda de genéricos)
• constructor y reglas de inicialización
• fundición de tipo seguro

Los tipos opcionales representan un tipo polimórfico que encapsula dos casos: el valor original o un valor ninguno . Se utilizan para tratar el caso de la nada ( nulo / nulo / etc.).

Los tipos opcionales son bastante comunes en los lenguajes funcionales y Swift tiene una sintaxis especial para tratarlos.
En Haskell, el tipo opcional se ve así:
data Maybe a = Nothing | Just a
En Swift, se ve así:

  enum Opcional  { 
     caso Ninguno
     caso algunos (T) 
 }

Pero esto generalmente se escribe como T? y se inicializa con un valor o nulo .

Los tipos opcionales se aplican a cualquier tipo en Swift porque la enumeración es genérica.
Entonces, ¿por qué es útil? Cuando define una variable de tipo String como var a:String tiene la garantía de que la variable a solo puede obtener valores en el rango posible de valores de String . No puede ser nulo . Eso es válido para todos los tipos Swift ( Int , Float , Double , Array , etc. y tipos personalizados).
Por lo tanto, el código anterior generará un error si la variable se usa antes de inicializarse: el compilador siempre se quejará de las variables o constantes no inicializadas antes de su uso.

Entonces, ¿qué sucede si no conoce el valor de alguna variable? O realmente quieres expresar la nada . Los opcionales entran en juego aquí.
Digamos que tiene una variable var a:Int?
Esta variable no tiene que inicializarse antes del uso, sino que el compilador lo obligará a verificar si a es nulo o no:

  si se deja sin envolver = a { 
     // usa Int sin envolver 
 } más { 
     // a es nulo 
 }

Si una función toma un Int y pasas un como Int? , obtendrá un error de tipo no coincidente. Este modelo funciona muy bien para garantizar la seguridad de la memoria. En Objective-C, todos los tipos de referencia pueden ser nulos, pero el compilador no te hace escribir todos y cada uno de los tipos de referencia antes de usarlos (porque eso sería realmente molesto). En Swift solo a veces tienes que modelar la nada .

Con la conversión de tipos obtienes el mismo comportamiento: el compilador te hace verificar si la conversión tiene éxito o no.

Por supuesto, hay algunas soluciones alternativas para estos requisitos de compilación, pero generalmente no se recomiendan y se usan solo debido a algunas limitaciones, casos extremos o casos en los que el programador realmente sabe lo que está haciendo.

Las reglas de constructor y de inicialización se refieren al mismo principio. No puede construir un objeto sin inicializarlo cuidadosamente. Los tipos no opcionales deben inicializarse correctamente. Además, al subclasificar, entran en juego algunas reglas especiales. Consulte el libro oficial de Swift para obtener más información.

También puede haber constructores defectuosos que devuelvan nil si fallaron al inicializar el objeto (digamos que falta un archivo en un disco) o T? Si el archivo tiene éxito. En cualquier caso, debe verificar si la inicialización se realizó correctamente.
• Tony Hoare: hay una historia famosa relacionada con los problemas de referencia nula llamada el error de mil millones de dólares de Tony Hoare.
• Nulabilidad y Objective-C – Blog Swift
Este año (2015) en WWDC, Apple anunció que agregaron algunas palabras clave al lenguaje Objective-C como nullable y no nulo para ayudar a la transición a Swift.

Tipificación estática

Esta es una característica en mi opinión.
Hacer que el compilador te ayude a cometer errores siempre es bueno. El compilador siempre le avisará cuando algo puede ser ambiguo o incorrecto en cuanto a tipo. La información es clave. Ser explícito cuando escribe código hace que su código sea menos propenso a errores.

Genéricos

Tener más información de tipo en tiempo de compilación le brinda más seguridad, expresividad, reutilización de código y rendimiento.
En Objective-C tenías NSArray en el que puedes empujar cualquier cosa, sin saber con certeza qué obtendrás cuando quieras recuperar algo de la matriz.
En Swift, los genéricos se implementan utilizando Reification y no Type borrado como Java. Puedes leer más sobre esto:
• Comparación de C Sharp y Java.

Este año (2015) en WWDC, Apple anunció que agregó soporte para genéricos livianos en Objective-C para ayudar a la transición a Swift.

Declaración de cambio

La declaración de cambio rápido es mucho más poderosa que en Objective-C (y muchos otros lenguajes). Puede activar Ints, Strings, Ranges, Tuples y cualquier otro tipo que pueda funcionar con el operador ~ =. También puede sobrecargar el operador ~ = para que pueda coincidir con el patrón sobre sus tipos personalizados.
La declaración de cambio rápido también es exhaustiva y le advierte si no trata todos los casos posibles. También puede usar el caso predeterminado para grandes conjuntos de valores posibles como Int, String, etc., pero esto es especialmente útil para las enumeraciones.
Por lo que recuerdo, esta es una de las características favoritas de Chris Lattner (el principal diseñador de idiomas).
Más sobre la declaración de cambio:
• Swift: el buen cambio del este

Enumeraciones

Las enumeraciones en Swift son súper geniales. Si no recuerdo mal, en una discusión en los foros de desarrolladores, Chris Lattner dijo que las enumeraciones son su característica más favorita del lenguaje.
Tienen una semántica de valores, pueden ajustarse a los protocolos, pueden ampliarse, pueden usar valores sin procesar para inicializarse (Int, String, etc.), pueden ser genéricos, pueden tener métodos estáticos y de instancia, pueden ser auto recurrentes y pueden tener valores asociados
Por ejemplo:

  enum Resultado  {
     case Success (T) // tipo genérico asociado
     Error de caso (cadena)

     func errorMessage () -> Cadena?  {
         interruptor (auto) {
         caso. Éxito (_):
             volver nulo
         caso .Error (dejar mensaje):
             mensaje de retorno
         }
     }
 }

 func doRequest () -> Resultado  {
     si (éxito) {
       return .Success (respuesta JSON)
     } más {
       return .Error (mensaje de error)
     }
 }

Enumeración recursiva:

  enum ArithmeticExpression {
     Número de caso (Int)
     Adición de caso indirecto (ArithmeticExpression, ArithmeticExpression)
     Multiplicación indirecta de casos (ArithmeticExpression, ArithmeticExpression)
 }

Valor y tipos de referencia

En Swift puede tener valores y tipos de referencia.
structs , enums , tuples siguen la semántica de valores ( las instancias de clase siguen la semántica de referencia). Esto significa que siguen la semántica de paso por valor.
Esto también significa que tiene la garantía de que no tendrá varias referencias apuntando al mismo objeto. Pasar por valor también puede producir un código más optimizado y ayudar a la programación para aplicaciones paralelas sin la necesidad de utilizar mecanismos de sincronización.
Hay una charla interesante de Andy Matuschak (ex empleado de Apple UIKit):
• Control de la complejidad en Swift por Andy Matuschak (Video)

Mutabilidad / Inmutabilidad

En swift, puede declarar que algo es mutable usando la palabra clave var e inmutable usando la palabra clave let.
Si utiliza un tipo de referencia let , no puede cambiar la referencia a esa instancia, pero puede cambiar la instancia.
Por otro lado, si usa let en una estructura o enumeración, ese valor es inmutable y no puede llamar a métodos de mutación en esos valores.
Si usamos var, tanto las referencias como los valores pueden cambiar. Los tipos de valor tienen métodos marcados con la palabra clave que muta si cambian el valor.
Esta característica ofrece oportunidades de optimización de seguridad y rendimiento para el compilador.

Extensiones de estructuras y enumeraciones

Puede extender fácilmente una estructura o enumeración existente (incluso si no fue definida por usted).
Por ejemplo:

  extensión CGRect {
     func rectByOffsetting (dx: CGFloat, dy: CGFloat) -> CGRect {/ * ... * /}
     desplazamiento de funciones mutantes (dx: CGFloat, dy: CGFloat) {/ * ... * /}
 }

Menos código, menos archivos.

Como mencionó, la sintaxis es mucho más ligera y solo necesita un archivo (.swift) sin que el .m / .h salte.

Protocolos con implementación predeterminada

La principal deficiencia de los protocolos, tanto en Swift como en Objective-C, es la falta de una forma integrada de proporcionar implementaciones predeterminadas para los métodos, como se podría lograr en otros idiomas con mixins o rasgos .
Por ejemplo:

  struct PackagingOptions: OptionSetType {
     let rawValue: Int
     init (rawValue: Int) {self.rawValue = rawValue}

     let let Box = PackagingOptions (rawValue: 1)
     static let Carton = PackagingOptions (rawValue: 2)
     let let Bag = PackagingOptions (rawValue: 4)
     static let Satchel = PackagingOptions (rawValue: 8)
     let estático BoxOrBag: PackagingOptions = [Box, Bag]
     let estático BoxOrCartonOrBag: PackagingOptions = [Box, Carton, Bag]
 }

Con solo adoptar el protocolo con un poco de código, se obtiene un gran conjunto de operaciones gratuitas implementadas de manera predeterminada en el protocolo como: isSubsetOf , isSupersetOf , isDisjointWith , restar , isEmpty , isStrictSupersetOf , isStrictSubsetOf , etc.
Más sobre programación orientada al protocolo:
• Programación orientada al protocolo en Swift
• ¿Por qué Apple enfatiza el desarrollo orientado al protocolo en Swift? Java ha soportado la interfaz durante mucho tiempo, ¿qué tiene de especial el protocolo de uso?

Parques infantiles rápidos

Puede usar los patios de juegos Swift para probar piezas de código muy rápido sin la necesidad de volver a compilar cada vez que un proyecto completo. Puedes ver los cambios en vivo en tiempo real. Esto es particularmente útil para la transición de Objective-C.
Ayuda a la creación rápida de prototipos y también ayuda a los principiantes a acercarse al idioma.
Aquí está Chris Lattner presentando los parques infantiles:

Fuente abierta

En WWDC 2015, Apple anunció que Swift se convertirá en código abierto para fin de año. Esto garantizará más transparencia, más apoyo de la comunidad, información privilegiada, etc. El lenguaje también estará disponible para Linux (y tal vez Windows en el futuro).
Esto hará que el lenguaje se use para el desarrollo completo de la pila. Imagine escribir una API web, una aplicación móvil y aplicaciones de escritorio, todo en Swift.

Despacho estático o dinámico

Al igual que muchos otros idiomas, Swift permite que una clase anule los métodos y propiedades declarados en sus superclases.

Esto significa que el programa tiene que determinar en tiempo de ejecución a qué método o propiedad se hace referencia y luego realizar una llamada indirecta o acceso indirecto.

Esta técnica, llamada despacho dinámico, aumenta la expresividad del lenguaje a costa de una sobrecarga constante de tiempo de ejecución para cada uso indirecto.
En el código sensible al rendimiento, dicha sobrecarga a menudo es indeseable. Hay maneras de mejorar el rendimiento al eliminar dicho dinamismo: la palabra clave final, el especificador de acceso privado y la optimización de todo el módulo.

Más sobre esto:
• Aumento del rendimiento al reducir el despacho dinámico – Blog Swift
• Perfilado en profundidad – minuto 29:22

Múltiples tipos de retorno

Puede usar tuplas para devolver múltiples resultados de una función:

  func foobar () -> (Int, String) {
   volver (5, "foobar")
 }

Soporte para el mecanismo de manejo de errores try-catch

QUE HACER

Sobrecarga del operador

Esta es la característica más controvertida de Swift. Agrega mucha potencia en manos del programador. Puede sobrecargar muchos operadores para un buen uso (como agregar dos estructuras CGRect o implementar el operador de igualdad para cumplir con el protocolo Equatable) o para un mal uso.

Esta es una característica que debe tratarse con mucha responsabilidad.
Por ejemplo:

  struct Vector2D {
     var x = 0.0, y = 0.0
 }

 func + (izquierda: Vector2D, derecha: Vector2D) -> Vector2D {
     devuelve Vector2D (x: left.x + right.x, y: left.y + right.y)
 }

Inferencia de tipos

Esta característica es más conveniente. El compilador sabe cómo inferir el tipo de una expresión en la mayoría de los casos. Donde tenga problemas, te avisará.
let a = 4 // this will be automatically inferred as Int
Sintaxis de bloque más fácil

La sintaxis de bloque es mucho mejor. Ya no es necesario ¿Cómo declaro un bloque en Objective-C? como una referencia. Todavía hay cierres en Swift, pero creo que simplemente no es necesario.

Tipos anidados

Puede definir nuevos tipos dentro de otros tipos.

Funciones curry

El concepto de curry es que, en lugar de aceptar múltiples argumentos, una función acepta solo uno y devuelve una función que acepta los argumentos restantes. La función devuelta también aceptará solo un argumento y devuelve otra función. Este proceso continúa hasta que se agoten todos los argumentos y solo nos quede un solo valor de retorno.

Esto es útil cuando no tiene todos los argumentos de la función a la vez.

Disponibilidad sintaxis

En Swift 2.0 hay una nueva sintaxis para verificar la compatibilidad API. Hay un conjunto de API que están anotadas con el objetivo mínimo de implementación requerido.

El compilador le avisará cuando intente llamar a las API marcadas con anotaciones de disponibilidad. Primero deberá verificar el objetivo mínimo de implementación para usar las API.
Ejemplo:

  si #disponible (iOS 9, *) {
     // usa UIStackView
 } más {
     // 🙁 utilizar diseño "Auto"
 }

Control de acceso

Existen las palabras clave pública, interna y privada que se utilizan para especificar el control de acceso para el código.

willSet y didSet

Para observar las propiedades, hay una buena sintaxis que le permite hacer eso sin la necesidad de anular por completo los establecedores / captadores.

  clase StepCounter {
     var totalSteps: Int = 0 {
         willSet (newTotalSteps) {
             println ("Acerca de establecer totalSteps en \ (newTotalSteps)")
         }
         didSet {
             if totalSteps> oldValue {
                 println ("Pasos agregados \ (totalSteps - oldValue)")
             }
         }
     }
 }

Hasta donde sé, Objective-C no tiene esencialmente limitaciones, solo inconvenientes o sintaxis poco elegante.