Acabamos de actualizar Xcode a 10.2 (de ahí iOS 12.2 SDK) y comenzamos a ver un comportamiento extraño con respecto al comportamiento de Swift Generics y Optionals . Hemos mantenido la versión de Swift en 4.2, por lo que no hay actualización de Swift 5. El único cambio fue actualizar a Xcode 10.2 desde Xcode 10.1.
Aquí hay un código de muestra que ilustra las rarezas. Los comentarios muestran lo que ha cambiado entre versiones. Idealmente, no debería haber ningún cambio.
class Phone<T> {} extension Phone { class func create(initial: T? = nil) -> Phone<T> { if let _ = initial { print("Regular: Unwrapping worked.") } return Phone() } } extension Phone where T == Void { class func create(initial: T? = nil) -> Phone<T> { if let _ = initial { print("T == Void: Unwrapping worked.") } return Phone() } } var phone: Phone<Int?> = Phone() var phone2: Phone<Int?> = Phone() var phone3: Phone<Int?> = Phone() // unwrapping works iOS 12.1, doesn't work in 12.2 phone = Phone.create(initial: Optional(nil)) // unwrapping works iOS 12.1, doesn't work in 12.2 phone2 = Phone.create(initial: Optional<Int?>(nil)) // doesn't compile in iOS 12.1, unwrapping works in iOS 12.2 phone3 = Phone.create(initial: Optional<Int>(nil)) // doesn't compile in iOS 12.1, unwrapping doesn't work in 12.2 (uses the T == Void function) let phone4 = Phone.create(initial: Optional(nil))Hemos revisado las notas de la versión de Xcode 10.2, pero no hemos detectado ningún cambio en los opcionales o genéricos. Es realmente difícil entender qué está causando este cambio de comportamiento entre versiones.
Es especialmente interesante cómo phone2 y phone3 se comportan de manera diferente. Hay algunas cosas extrañas que suceden en el ejemplo de código anterior, por lo que la pregunta es si alguien sabe qué podría haber causado los cambios de comportamiento en esta versión.
Esto se debe a SE-0213: Inicialización literal a través de coerción , lo que significa que el compilador ahora trata Optional(nil) como nil as Optional . Anteriormente, con Optional(nil) , obtendría un valor nil envuelto , por ejemplo, Int??.some(nil) , sin embargo, ahora solo obtiene nil .
Entonces por lo siguiente:
let phone: Phone<Int?> = Phone.create(initial: Optional(nil))el compilador lo está tratando como:
let phone: Phone<Int?> = Phone.create(initial: nil as Optional)que es equivalente a:
let phone: Phone<Int?> = Phone.create(initial: nil) ¿Porque ha especificado que el parámetro genérico T sea Int? , el parámetro initial: toma un Int?? . Por lo tanto, al pasar nil , está pasando Int??.none y, por lo tanto, falla el desenvolvimiento.
Una forma de restaurar el comportamiento anterior es especificar .init explícitamente para obligar al compilador a llamar al inicializador:
let phone: Phone<Int?> = Phone.create(initial: Optional.init(nil)) Ahora está pasando un Int??.some(nil) al parámetro y el desenvolvimiento se realiza correctamente.
Sin embargo, me preguntaría por qué estás tratando con opciones doblemente envueltas en primer lugar; recomiendo encarecidamente evitarlas a menos que sea absolutamente necesario.