Ответ 1
Эта ошибка решена в Xcode 7 и Swift 2
Я попытался довести эту проблему до ее простейшей формы следующим образом.
Xcode Version 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 type '(rawValue: IntegerLiteralConvertible)' to type 'MyEnum?'
В Руководство по быстрому языку:
Если вы определяете перечисление с типом raw-value, перечисление автоматически получает инициализатор, который принимает значение типа необработанных значений (в качестве параметра с именем
rawValue
) и возвращает либо элемент перечисления, либоnil
.
Пользовательский инициализатор для MyEnum
был определен в расширении для проверки того, был ли инициализатор исходного значения перечисления удален из-за следующего случая из Руководство по языку. Однако он достигает того же результата ошибки.
Обратите внимание, что если вы определяете пользовательский инициализатор для типа значения, у вас больше не будет доступа к инициализатору по умолчанию (или инициализатору по умолчанию, если это структура) для этого типа. [...]
Если вы хотите, чтобы ваш настраиваемый тип значения был инициализирован инициализатором по умолчанию и инициализатором по порядку, а также с вашими собственными инициализаторами, напишите свои пользовательские инициализаторы в расширении, а не как часть исходной реализации типов значений.
Перемещение определения перечисления на MyClass.swift
устраняет ошибку для bar
, но не для foo
.
Удаление пользовательского инициализатора устраняет обе ошибки.
Одним из способов решения проблемы является включение следующей функции в определение перечисления и использование ее вместо предоставленного инициализатора исходных значений. Таким образом, кажется, что добавление пользовательского инициализатора имеет аналогичный эффект для маркировки инициализатора исходного значения private
.
init?(raw: Int) {
self.init(rawValue: raw)
}
Явное объявление соответствия протокола RawRepresentable
в MyClass.swift
разрешает встроенную ошибку для bar
, но приводит к ошибке компоновщика о повторяющихся символах (поскольку перечисления типа raw-value неявно соответствуют 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
}
}
Да, это раздражающая проблема. Я сейчас работаю над ним, используя функцию глобального масштаба, которая действует как factory, т.е.
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
}
}
Добавьте это в свой код:
extension MyEnum {
init?(rawValue: Int) {
switch rawValue {
case 0: self = .Zero
case 1: self = .One
case 2: self = .Two
default: return nil
}
}
}