programing

사용자 지정 이니셜라이저가 있는 Swift 열거형은 rawValue 이니셜라이저를 잃게 됩니다.

sourcetip 2023. 7. 31. 22:28
반응형

사용자 지정 이니셜라이저가 있는 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. 다음에 대한 사용자 정의 초기화자MyEnumLanguage Guide에서 다음과 같은 경우로 인해 열거형의 원시 값 이니셜라이저가 제거되는지 테스트하기 위해 확장에서 정의되었습니다.그러나 동일한 오류 결과를 얻을 수 있습니다.

    값 유형에 대한 사용자 정의 이니셜라이저를 정의하면 해당 유형에 대한 기본 이니셜라이저(또는 구조인 경우 멤버별 이니셜라이저)에 더 이상 액세스할 수 없습니다. [...]
    사용자 정의 값 유형을 기본 이니셜라이저 및 멤버별 이니셜라이저와 함께 초기화할 수 있도록 하려면 사용자 정의 값 유형의 원래 구현의 일부가 아닌 확장에 사용자 정의 값 유형을 작성합니다.

  3. 열거형 정의 이동MyClass.swift다음에 대한 오류 해결bar하지만 때문은 아닙니다.foo.

  4. 사용자 지정 초기화 프로그램을 제거하면 두 오류가 모두 해결됩니다.

  5. 한 가지 해결 방법은 열거형 정의에 다음 함수를 포함하고 제공된 원시 값 이니셜라이저 대신 사용하는 것입니다.따라서 사용자 정의 이니셜라이저를 추가하면 원시 값 이니셜라이저를 표시하는 것과 유사한 효과가 있는 것 같습니다.private.

    init?(raw: Int) {
        self.init(rawValue: raw)
    }
    
  6. 다음에 대한 프로토콜 준수를 명시적으로 선언RawRepresentableMyClass.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
        }
    }
}

언급URL : https://stackoverflow.com/questions/27390989/swift-enum-with-custom-initializer-loses-rawvalue-initializer

반응형