swift - the - La enumeración rápida con inicializador personalizado pierde el inicializador rawValue
swift4 enum (6)
Agregue esto a su código:
extension MyEnum {
init?(rawValue: Int) {
switch rawValue {
case 0: self = .Zero
case 1: self = .One
case 2: self = .Two
default: return nil
}
}
}
He tratado de reducir este problema a su forma más simple con lo siguiente.
Preparar
Xcode versión 6.1.1 (6A2008a)
Una enumeración definida en
MyEnum.swift
:
internal enum MyEnum: Int {
case Zero = 0, One, Two
}
extension MyEnum {
init?(string: String) {
switch string.lowercaseString {
case "zero": self = .Zero
case "one": self = .One
case "two": self = .Two
default: return nil
}
}
}
y código que inicializa la enumeración en otro archivo,
MyClass.swift
:
internal class MyClass {
let foo = MyEnum(rawValue: 0) // Error
let fooStr = MyEnum(string: "zero")
func testFunc() {
let bar = MyEnum(rawValue: 1) // Error
let barStr = MyEnum(string: "one")
}
}
Error
Xcode me da el siguiente error cuando intento inicializar
MyEnum
con su inicializador de valor bruto:
Cannot convert the expression''s type ''(rawValue: IntegerLiteralConvertible)'' to type ''MyEnum?''
Notas
-
Según la guía de idiomas Swift :
Si define una enumeración con un tipo de valor sin procesar, la enumeración recibe automáticamente un inicializador que toma un valor del tipo del valor sin procesar (como un parámetro llamado
rawValue
) y devuelve un miembro de enumeración onil
. -
El inicializador personalizado para
MyEnum
se definió en una extensión para probar si el inicializador de valor sin procesar del enum se estaba eliminando debido al siguiente caso de la Guía de idiomas . Sin embargo, logra el mismo resultado de error.Tenga en cuenta que si define un inicializador personalizado para un tipo de valor, ya no tendrá acceso al inicializador predeterminado (o al inicializador miembro, si es una estructura) para ese tipo. [...]
Si desea que su tipo de valor personalizado se pueda inicializar con el inicializador predeterminado y el inicializador miembro, y también con sus propios inicializadores personalizados, escriba sus inicializadores personalizados en una extensión en lugar de como parte de la implementación original del tipo de valor. -
Mover la definición de enumeración a
MyClass.swift
resuelve el error parabar
pero no parafoo
. -
Eliminar el inicializador personalizado resuelve ambos errores.
-
Una solución alternativa es incluir la siguiente función en la definición de enumeración y usarla en lugar del inicializador de valor bruto proporcionado. Por lo tanto, parece que agregar un inicializador personalizado tiene un efecto similar a marcar el inicializador de valor sin procesar como
private
.init?(raw: Int) { self.init(rawValue: raw) }
-
La declaración explícita de conformidad del protocolo con
RawRepresentable
enMyClass.swift
resuelve el error en línea para labar
, pero da como resultado un error de enlace sobre símbolos duplicados (porque las enumeraciones de tipo de valor sin procesar se ajustan implícitamente aRawRepresentable
).extension MyEnum: RawRepresentable {}
¿Alguien puede proporcionar un poco más de información sobre lo que está sucediendo aquí? ¿Por qué no se puede acceder al inicializador de valor sin procesar?
Este error se resuelve en Xcode 7 y Swift 2
Esto funciona para Swift 4 en Xcode 9.2 junto con mi EnumSequence :
enum Word: Int, EnumSequenceElement, CustomStringConvertible {
case apple, cat, fun
var description: String {
switch self {
case .apple:
return "Apple"
case .cat:
return "Cat"
case .fun:
return "Fun"
}
}
}
let Words: [String: Word] = [
"A": .apple,
"C": .cat,
"F": .fun
]
extension Word {
var letter: String? {
return Words.first(where: { (_, word) -> Bool in
word == self
})?.key
}
init?(_ letter: String) {
if let word = Words[letter] {
self = word
} else {
return nil
}
}
}
for word in EnumSequence<Word>() {
if let letter = word.letter, let lhs = Word(letter), let rhs = Word(letter), lhs == rhs {
print("/(letter) for /(word)")
}
}
Salida
A for Apple
C for Cat
F for Fun
Incluso puede hacer que el código sea más simple y útil sin
switch
casos, de esta manera no necesita agregar más casos cuando agrega un nuevo tipo.
enum VehicleType: Int, CustomStringConvertible {
case car = 4
case moped = 2
case truck = 16
case unknown = -1
// MARK: - Helpers
public var description: String {
switch self {
case .car: return "Car"
case .truck: return "Truck"
case .moped: return "Moped"
case .unknown: return "unknown"
}
}
static let all: [VehicleType] = [car, moped, truck]
init?(rawDescription: String) {
guard let type = VehicleType.all.first(where: { description == rawDescription })
else { return nil }
self = type
}
}
Sí, este es un problema molesto. Actualmente estoy trabajando en esto usando una función de alcance global que actúa como una fábrica, es decir
func enumFromString(string:String) -> MyEnum? {
switch string {
case "One" : MyEnum(rawValue:1)
case "Two" : MyEnum(rawValue:2)
case "Three" : MyEnum(rawValue:3)
default : return nil
}
}
extension TemplateSlotType {
init?(rawString: String) {
// Check if string contains ''carrousel''
if rawString.rangeOfString("carrousel") != nil {
self.init(rawValue:"carrousel")
} else {
self.init(rawValue:rawString)
}
}
}
En su caso, esto resultaría en la siguiente extensión:
extension MyEnum {
init?(string: String) {
switch string.lowercaseString {
case "zero":
self.init(rawValue:0)
case "one":
self.init(rawValue:1)
case "two":
self.init(rawValue:2)
default:
return nil
}
}
}