自定义初始值设定项的Swift枚举失去了rawValue初始值设定项

我试图把这个问题简化成最简单的forms,

build立

Xcode版本6.1.1(6A2008a)

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 } } } 

以及在另一个文件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") } } 

错误

当试图用它的原始值初始值设定项初始化MyEnum时,Xcode给了我下面的错误:

 Cannot convert the expression's type '(rawValue: IntegerLiteralConvertible)' to type 'MyEnum?' 

笔记

  1. 根据Swift语言指南 :

    如果使用原始值types定义枚举,则枚举将自动接收一个初始值设定项,该初始值设定项将采用原始值types的值(作为参数称为rawValue ),并返回枚举成员或nil

  2. MyEnum的自定义初始值MyEnum是在扩展中定义的,以testing枚举的原始值初始值设定项是否因语言指南中的以下情况而被删除。 但是,它达到了相同的错误结果。

    请注意,如果为某个值types定义了一个自定义初始值设定项,则将不再有权访问该types的默认初始值设定项(或成员初始值设定项,如果它是结构体)。 […]
    如果您希望自定义值types可以使用默认初始值设定项和成员初始值设定项以及您自己的自定义初始值设定项进行初始化,请将自定义初始值设定项编写为扩展名,而不是作为值types原始实现的一部分。

  3. 将枚举定义移动到MyClass.swift可以解决bar的错误,但不能解决foo

  4. 删除自定义初始值设定项解决了这两个错误。

  5. 一种解决方法是在枚举定义中包含以下函数,并使用它来代替提供的原始值初始值设定项。 所以看起来好像添加一个自定义初始值设定项与将原始值初始值设定项private标记的效果类似。

     init?(raw: Int) { self.init(rawValue: raw) } 
  6. MyClass.swift明确声明协议符合RawRepresentable ,可以解决bar的内联错误,但会导致关于重复符号的链接器错误(因为原始值types的枚举符合RawRepresentable )。

     extension MyEnum: RawRepresentable {} 

任何人都可以提供一些更深入的了解这里发生了什么? 为什么不能使用原始值初始值设定项?

这个bug在Xcode 7和Swift 2中解决了

 extension TemplateSlotType { init?(rawString: String) { // Check if string contains 'carrousel' if rawString.rangeOfString("carrousel") != nil { self.init(rawValue:"carrousel") } else { self.init(rawValue:rawString) } } } 

在你的情况下,这将导致以下扩展:

 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 } } } 

是的,这是一个令人讨厌的问题。 我目前正在使用一个全球范围的function,作为一个工厂,即

 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 } } 

你甚至可以使代码更简单和有用,无需switch情况下,这样你就不需要添加更多的情况下,当你添加一个新的types。

 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 } } 

添加到您的代码:

 extension MyEnum { init?(rawValue: Int) { switch rawValue { case 0: self = .Zero case 1: self = .One case 2: self = .Two default: return nil } } }