使用自定义初始化程序的Swift枚举会丢失rawValue初始化程序


95

我已尝试通过以下方法将此问题简化为最简单的形式。

建立

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语言指南》

    如果您使用原始值类型定义枚举,则枚举会自动接收一个初始化器,该初始化器采用原始值类型的值(称为rawValue)作为参数,并返回枚举成员或nil

  2. MyEnum在扩展程序中定义了的自定义初始化程序,以测试是否由于《语言指南》中的以下情况而删除了枚举的原始值初始化程序。但是,它获得相同的错误结果。

    请注意,如果您为值类型定义自定义初始化程序,则将不再有权使用该类型的默认初始化程序(或成员初始化程序,如果它是结构)。[...]
    如果您希望自定义值类型可以使用默认的初始化器和成员初始化器以及您自己的自定义初始化器进行初始化,请在扩展名中编写自定义初始化器,而不是将其作为值类型的原始实现的一部分。

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

  4. 删除自定义初始化程序可以解决这两个错误。

  5. 一种解决方法是在枚举定义中包括以下功能,并代替提供的原始值初始化程序使用它。因此,似乎添加自定义初始化程序与标记原始值初始化程序具有相似的效果private

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

    extension MyEnum: RawRepresentable {}

任何人都可以对这里发生的事情提供更多的见解吗?为什么无法访问原始值初始化程序?


您应该为此提交一个错误-默认初始化程序应具有internal作用域(或至少与类型匹配),而不是作用域private
Nate Cook

我遇到了完全相同的问题。创建自定义初始化程序后,默认的初始化程序就消失了
Yariv Nissim 2014年

对我来说像臭虫。
akashivskyy 2014年

2
感谢您证实我的怀疑。这已被记录为错误。
nickgraef

5号为我做到了。
Andrew Duncan 2015年

Answers:


25

此错误已在Xcode 7和Swift 2中解决


24
此类答案是通过关联票证的链接获得的,以便将来的访问者可以检查事件的状态。
拉斐尔

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

7

您甚至可以在不使用大小写的switch情况下使代码更简单,更有用,这样,您在添加新类型时就无需添加更多大小写。

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

1

是的,这是一个令人讨厌的问题。我目前正在使用充当工厂的全局作用域函数来解决它,即

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

0

这适用于Xcode 9.2上的Swift 4和我的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)")
    }
}

输出量

A for Apple
C for Cat
F for Fun

-1

将此添加到您的代码:

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

您可以扩展Int吗?似乎更容易。
ericgu
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.