Antes de que pueda describir los casos de uso de los opcionales implícitamente sin envolver, ya debe comprender qué son los opcionales y los opcionales implícitamente sin envolver en Swift. Si no lo hace, le recomiendo que primero lea mi artículo sobre opciones
Cuándo usar una opción opcional sin envolver implícitamente
Hay dos razones principales por las que uno crearía un Opcional implícitamente sin envolver. Todo tiene que ver con la definición de una variable a la que nunca se accederá nil
porque, de lo contrario, el compilador Swift siempre lo obligará a desenvolver explícitamente un Opcional.
1. Una constante que no se puede definir durante la inicialización
Cada constante de miembro debe tener un valor para cuando se complete la inicialización. A veces, una constante no se puede inicializar con su valor correcto durante la inicialización, pero aún se puede garantizar que tenga un valor antes de acceder.
El uso de una variable Opcional soluciona este problema porque un Opcional se inicializa automáticamente nil
y el valor que eventualmente contendrá seguirá siendo inmutable. Sin embargo, puede ser una molestia desenvolver constantemente una variable que usted sabe con certeza que no es nula. Los opcionales sin envoltura implícitamente logran los mismos beneficios que un opcional con el beneficio adicional de que uno no tiene que desenvolverlo explícitamente en todas partes.
Un gran ejemplo de esto es cuando una variable miembro no se puede inicializar en una subclase de UIView hasta que se carga la vista:
class MyView: UIView {
@IBOutlet var button: UIButton!
var buttonOriginalWidth: CGFloat!
override func awakeFromNib() {
self.buttonOriginalWidth = self.button.frame.size.width
}
}
Aquí, no puede calcular el ancho original del botón hasta que se cargue la vista, pero sabe que awakeFromNib
se llamará antes que cualquier otro método en la vista (que no sea la inicialización). En lugar de forzar que el valor se desenvuelva explícitamente sin sentido en toda su clase, puede declararlo como Opcionalmente sin envolver opcional.
2. Cuando su aplicación no puede recuperarse de un ser variable nil
Esto debería ser extremadamente raro, pero si su aplicación no puede continuar ejecutándose si se nil
accede a una variable , sería una pérdida de tiempo molestarse en probarla nil
. Normalmente, si tiene una condición que debe ser absolutamente cierta para que su aplicación continúe ejecutándose, usaría un assert
. Un Opcional implícitamente sin envolver tiene una aserción para nil integrada directamente en él. Incluso entonces, a menudo es bueno desenvolver lo opcional y usar una afirmación más descriptiva si es nula.
Cuándo no utilizar un opcional implícitamente desenvuelto
1. Variables de miembro calculadas perezosamente
A veces tiene una variable miembro que nunca debe ser nula, pero no se puede establecer en el valor correcto durante la inicialización. Una solución es usar un Opcional implícitamente sin envolver, pero una mejor manera es usar una variable perezosa:
class FileSystemItem {
}
class Directory : FileSystemItem {
lazy var contents : [FileSystemItem] = {
var loadedContents = [FileSystemItem]()
// load contents and append to loadedContents
return loadedContents
}()
}
Ahora, la variable miembro contents
no se inicializa hasta la primera vez que se accede a ella. Esto le da a la clase la oportunidad de llegar al estado correcto antes de calcular el valor inicial.
Nota: Esto puede parecer contradecir el n. ° 1 desde arriba. Sin embargo, hay una distinción importante que hacer. Lo buttonOriginalWidth
anterior debe establecerse durante viewDidLoad para evitar que alguien cambie el ancho de los botones antes de acceder a la propiedad.
2. En todas partes
En su mayor parte, se deben evitar las opciones implícitas sin envolver porque si se usa por error, toda la aplicación se bloqueará cuando se acceda a ella nil
. Si alguna vez no está seguro de si una variable puede ser nula, use siempre un Opcional normal de forma predeterminada. Desenvolver una variable que nunca es segura no nil
hace mucho daño.
if someOptional
.hasValue
se define directamente en Opcional. Prefiero la semánticahasValue
a la de!= nil
. Siento que es mucho más comprensible para los nuevos programadores que no han usadonil
en otros idiomas.hasValue
Es mucho más lógico quenil
.hasValue
fue sacado de la versión beta 6. Sin embargo, Ash lo devolvió ... github.com/AshFurrow/hasValueLos opcionales implícitamente sin envolver son útiles para presentar una propiedad como no opcional cuando realmente necesita ser opcional bajo las cubiertas. Esto es a menudo necesario para "atar el nudo" entre dos objetos relacionados, cada uno de los cuales necesita una referencia al otro. Tiene sentido cuando ninguna de las referencias es realmente opcional, pero una de ellas debe ser nula mientras se inicializa el par.
Por ejemplo:
Cualquier
B
instancia siempre debe tener unamyBuddyA
referencia válida , por lo que no queremos que el usuario la trate como opcional, pero necesitamos que sea opcional para poder construir unaB
antes de que tengamos unaA
referencia.¡SIN EMBARGO! Este tipo de requisito de referencia mutua es a menudo una indicación de acoplamiento apretado y diseño deficiente. Si se encuentra confiando en opciones implícitamente desenvueltas, probablemente debería considerar la refactorización para eliminar las dependencias cruzadas.
fuente
@IBOutlet
weak
declaración y elimine "sin crear un ciclo de retención fuerte"Las opciones implícitamente desenvueltas son un compromiso pragmático para hacer que el trabajo en un entorno híbrido que tiene que interoperar con los marcos de Cocoa existentes y sus convenciones sea más agradable, al tiempo que permite la migración gradual hacia un paradigma de programación más seguro, sin punteros nulos, implementado por el compilador Swift.
El libro de Swift, en el capítulo Lo básico , la sección Opciones implícitas sin envolver dice:
Esto se reduce a los casos de uso donde la no-
nil
integridad de las propiedades se establece a través de la convención de uso, y el compilador no puede imponerla durante la inicialización de la clase. Por ejemplo, lasUIViewController
propiedades que se inicializan a partir de NIB o Storyboards, donde la inicialización se divide en fases separadas, pero luegoviewDidLoad()
puede suponer que las propiedades generalmente existen. De lo contrario, para satisfacer al compilador, tenía que usar el desenvolvimiento forzado , el enlace opcional o el encadenamiento opcional solo para ocultar el propósito principal del código.La parte anterior del libro Swift se refiere también al capítulo Conteo automático de referencias :
Esto se reduce a las peculiaridades de no ser un lenguaje recolectado de basura, por lo tanto, la ruptura de los ciclos de retención depende de usted como programador y las opciones implícitamente desenvueltas son una herramienta para ocultar esta peculiaridad.
Eso cubre el "¿Cuándo usar opciones opcionales implícitamente sin envolver en su código?" pregunta. Como desarrollador de aplicaciones, los encontrará principalmente en firmas de método de bibliotecas escritas en Objective-C, que no tiene la capacidad de expresar tipos opcionales.
De Usar Swift con Cocoa y Objective-C, sección Trabajar con nil :
... y más allá yacía
fuente
Los ejemplos simples de una línea (o varias líneas) no cubren muy bien el comportamiento de los opcionales; sí, si declara una variable y le proporciona un valor de inmediato, no tiene sentido en un opcional.
El mejor caso que he visto hasta ahora es la configuración que ocurre después de la inicialización del objeto, seguida del uso que está "garantizado" para seguir esa configuración, por ejemplo, en un controlador de vista:
Sabemos
printSize
que se llamará después de cargar la vista: es un método de acción conectado a un control dentro de esa vista, y nos aseguramos de no llamarlo de otra manera. Por lo tanto, podemos ahorrarnos algunas comprobaciones / enlaces opcionales con el!
. Swift no puede reconocer esa garantía (al menos hasta que Apple resuelva el problema de detención), por lo que le dice al compilador que existe.Sin embargo, esto rompe la seguridad del tipo hasta cierto punto. Cualquier lugar donde tenga una opción implícitamente desenvuelta es un lugar en el que su aplicación puede bloquearse si su "garantía" no siempre se cumple, por lo que es una característica que debe usar con moderación. Además, usar
!
todo el tiempo suena como si estuvieras gritando, y a nadie le gusta eso.fuente
CGSizeZero
puede ser un buen valor centinela en el uso en el mundo real. Pero, ¿qué pasa si tiene un tamaño cargado desde la punta que en realidad podría ser cero? Luego, usarloCGSizeZero
como centinela no te ayuda a distinguir entre un valor que no se establece y se establece en cero. Por otra parte, esto se aplica igualmente bien a otros tipos cargados desde punta (o en cualquier otro lugar despuésinit
): las cadenas, las referencias a subvistas, etc.let foo? = 42
, no , más bienlet foo! = 42
). Esto no aborda eso. (Esta puede ser una respuesta relevante sobre los opcionales, claro, pero no sobre los opcionales implícitamente sin envolver que son un animal diferente / relacionado.)Apple da un gran ejemplo en El lenguaje de programación rápido -> Conteo automático de referencias -> Resolución de ciclos de referencia fuertes entre instancias de clase -> Referencias no deseadas y propiedades opcionales implícitamente desenvueltas
fuente
La justificación de los opcionales implícitos es más fácil de explicar al observar primero la justificación del desenvolvimiento forzado.
Desenvoltura forzada de un opcional (implícito o no), utilizando el! operador, significa que está seguro de que su código no tiene errores y que el opcional ya tiene un valor donde se está desenvolviendo. Sin el ! operador, probablemente solo afirmaría con un enlace opcional:
que no es tan bueno como
Ahora, las opciones implícitas le permiten expresar que tiene una opción que espera que siempre tenga un valor cuando se desenvuelve, en todos los flujos posibles. Por lo tanto, va un paso más allá para ayudarlo, ¡al relajar el requisito de escribir el! para desenvolver cada vez, y asegurarse de que el tiempo de ejecución seguirá siendo un error en caso de que sus suposiciones sobre el flujo sean incorrectas.
fuente
init
(lo que quizás ni siquiera implemente), pero ' re garantizado para ser configurado después deawakeFromNib
/viewDidLoad
.Si lo sabe con certeza, un valor devuelto por un opcional en lugar de uno opcional
nil
, implícitamente desenvuelto, utiliza para capturar directamente esos valores de los opcionales y los no opcionales no pueden.Entonces esta es la diferencia entre el uso de
let someString : String!
ylet someString : String
fuente
Creo que
Optional
es un mal nombre para esta construcción que confunde a muchos principiantes.Otros lenguajes (Kotlin y C #, por ejemplo) usan el término
Nullable
, y hace que sea mucho más fácil de entender esto.Nullable
significa que puede asignar un valor nulo a una variable de este tipo. Entonces, si es asíNullable<SomeClassType>
, puede asignarle valores nulos, si es soloSomeClassType
, no puede. Así es como funciona Swift.¿Por qué usarlos? Bueno, a veces necesitas tener valores nulos, por eso. Por ejemplo, cuando sabe que desea tener un campo en una clase, pero no puede asignarlo a nada cuando está creando una instancia de esa clase, pero lo hará más adelante. No daré ejemplos, porque la gente ya los ha proporcionado aquí. Solo estoy escribiendo esto para dar mis 2 centavos.
Por cierto, le sugiero que mire cómo funciona esto en otros idiomas, como Kotlin y C #.
Aquí hay un enlace que explica esta característica en Kotlin: https://kotlinlang.org/docs/reference/null-safety.html
Otros lenguajes, como Java y Scala, tienen
Optional
s, pero funcionan de manera diferente aOptional
s en Swift, porque los tipos de Java y Scala son anulables por defecto.En general, creo que esta característica debería haber sido nombrada
Nullable
en Swift, y noOptional
...fuente
Implicitly Unwrapped Optional
es un azúcar sintácticoOptional
que no obliga a un programador a desenvolver una variable. Se puede usar para una variable que no se puede inicializar durantetwo-phase initialization process
e implica que no es nula. Esta variable se comporta como no nula pero en realidad es una variable opcional . Un buen ejemplo es: puntos de venta de Interface BuilderOptional
generalmente son preferiblesfuente