사용자 지정 이니셜 라이저가있는 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?'
노트
당 스위프트 언어 가이드 :
원시 값 유형으로 열거 형을 정의하면 열거 형은 원시 값 유형의 값 (이라는 매개 변수)을 사용하는 이니셜 라이저를 자동으로 수신
rawValue
하고 열거 형 멤버 또는nil
.에 대한 사용자 정의 이니셜
MyEnum
라이저는 언어 가이드 의 다음 사례로 인해 열거 형의 원시 값 이니셜 라이저가 제거되었는지 여부를 테스트하기 위해 확장에 정의되었습니다 . 그러나 동일한 오류 결과를 얻습니다.값 유형에 대해 사용자 정의 이니셜 라이저를 정의하면 해당 유형에 대한 기본 이니셜 라이저 (또는 구조 인 경우 멤버 단위 이니셜 라이저)에 더 이상 액세스 할 수 없습니다. [...]
기본 이니셜 라이저와 멤버 별 이니셜 라이저 및 사용자 지정 이니셜 라이저를 사용하여 사용자 지정 값 형식을 초기화하려면 값 형식의 원래 구현의 일부가 아닌 확장에 사용자 지정 이니셜 라이저를 작성합니다.Moving the enum definition to
MyClass.swift
resolves the error forbar
but not forfoo
.Removing the custom initializer resolves both errors.
One workaround is to include the following function in the enum definition and use it in place of the provided raw-value initializer. So it seems as if adding a custom initializer has a similar effect to marking the raw-value initializer
private
.init?(raw: Int) { self.init(rawValue: raw) }
Explicitly declaring protocol conformance to
RawRepresentable
inMyClass.swift
resolves the inline error forbar
, but results in a linker error about duplicate symbols (because raw-value type enums implicitly conform toRawRepresentable
).extension MyEnum: RawRepresentable {}
Can anyone provide a little more insight into what's going on here? Why isn't the raw-value initializer accessible?
This bug is solved in Xcode 7 and 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)
}
}
}
In your case this would result in the following extension:
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
}
}
}
You can even make the code simpler and useful without switch
cases, this way you don't need to add more cases when you add a new type.
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
}
}
Yeah this is an annoying issue. I'm currently working around it using a global-scope function that acts as a factory, i.e.
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
}
}
This works for Swift 4 on Xcode 9.2 together with my 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)")
}
}
Output
A for Apple
C for Cat
F for Fun
Add this to your code:
extension MyEnum {
init?(rawValue: Int) {
switch rawValue {
case 0: self = .Zero
case 1: self = .One
case 2: self = .Two
default: return nil
}
}
}
'developer tip' 카테고리의 다른 글
분산 트랜잭션을 시작할 수 없습니다. (0) | 2020.09.08 |
---|---|
GZIP에 비해 압축 브라우저 호환성 및 이점 축소 (0) | 2020.09.08 |
Interface Builder의 "너비가 높이와 같음"제약 조건 (0) | 2020.09.07 |
실행 파일을 실행하는 데 필요한 .NET Framework 버전을 확인하는 방법은 무엇입니까? (0) | 2020.09.07 |
임의 정밀도 산술 설명 (0) | 2020.09.07 |