[swift] 사용자 지정 이니셜 라이저가있는 Swift 열거 형에서 rawValue 이니셜 라이저가 손실 됨

나는이 문제를 다음과 같이 가장 단순한 형태로 요약하려고 노력했다.

설정

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

오류

Xcode는 MyEnum원시 값 이니셜 라이저 로 초기화하려고 할 때 다음 오류를 표시합니다 .

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

노트

  1. 스위프트 언어 가이드 :

    원시 값 유형으로 열거 형을 정의하는 경우 열거 형은 원시 값 유형의 값 (이라는 매개 변수)을 사용하는 이니셜 라이저를 자동으로 수신 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 {}

누구든지 여기서 무슨 일이 일어나고 있는지에 대해 조금 더 통찰력을 줄 수 있습니까? 원시 값 이니셜 라이저에 액세스 할 수없는 이유는 무엇입니까?



답변

이 버그는 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
        }
    }
}


답변

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


답변

예, 이것은 성가신 문제입니다. 저는 현재 공장 역할을하는 전역 범위 함수를 사용하여 작업하고 있습니다.

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


답변

이것은 내 EnumSequence 와 함께 Xcode 9.2의 Swift 4에서 작동합니다 .

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


답변

코드에 다음을 추가하십시오.

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


답변