私はこの問題を次のように最も単純な形に要約しようとしました。
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
をraw値初期化子で初期化しようとすると、次のエラーが表示されます。
Cannot convert the expression's type '(rawValue: IntegerLiteralConvertible)' to type 'MyEnum?'
Raw-value型で列挙を定義すると、列挙は自動的にraw_valueの型の値(
rawValue
というパラメーターとして)を受け取り、列挙メンバーまたはnil
のいずれかを返す初期化子を受け取ります。
MyEnum
のカスタム初期化子は、 言語ガイド からの次のケースのために、列挙型の未加工値初期化子が削除されたかどうかをテストする拡張機能で定義されました。ただし、同じエラー結果が得られます。
値型のカスタム初期化子を定義すると、その型のデフォルトの初期化子(または構造体の場合はメンバーごとの初期化子)にアクセスできなくなることに注意してください。 [...]
デフォルトの初期化子とメンバーごとの初期化子、および独自のカスタム初期化子でカスタム値型を初期化できるようにする場合は、値型の元の実装の一部としてではなく、拡張でカスタム初期化子を記述します。
Enum定義をMyClass.Swift
に移動すると、bar
のエラーは解決されますが、foo
のエラーは解決されません。
カスタム初期化子を削除すると、両方のエラーが解決します。
回避策の1つは、列挙型定義に次の関数を含め、提供されたraw値初期化子の代わりにそれを使用することです。そのため、カスタム初期化子を追加すると、生の値の初期化子private
をマークするのと同様の効果があるようです。
init?(raw: Int) {
self.init(rawValue: raw)
}
MyClass.Swift
のRawRepresentable
へのプロトコル準拠を明示的に宣言すると、bar
のインラインエラーが解決されますが、重複シンボルに関するリンカーエラーが発生します(raw値型の列挙型は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
}
}
これは、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