Swift: литье Любое в массив объектов протокола
Существует протокол:
protocol Valuable {
func value() -> Int
}
и класс, который реализует протокол:
class Value: Valuable {
private let v: Int
init(value: Int) {
v = value
}
func value() -> Int {
return v
}
}
Существует массив объектов Value, хранящихся в переменной Any type:
let any: Any = [Value(value: 1), Value(value: 2), Value(value: 3)]
Можно передать Any в значение [Value]:
let arrayOfValue = any as? [Value] // [1, 2, 3]
Почему это невозможно для случая Любое [Ценное]?
let arrayOfValuable = any as! [Valuable] // compiler error BAD INSTRUCTION
let arrayOfValuable2 = any as? [Valuable] // nil
Ответы
Ответ 1
Обновлено: В Swift3 можно полностью отличить [Any]
от [Valuable]
. Листинг будет успешным, если все элементы в массиве могут быть заброшены; литой не удастся иначе.
var strings: [Any] = ["cadena"]
var mixed: [Any] = ["cadena", 12]
strings as! [String] // ["cadena"]
mixed as? [String] // nil
mixed as! [String] // Error! Could not cast value...
Ранее с Swift 2: Чтобы сделать [Valuable]
из [Any]
, он должен выполняться вручную с помощью функций типа map
, как объяснили другие ответы.
В настоящее время нет ковариации или контравариантности с дженериками в Swift (начиная с Swift 2). Это означает, что массивы разных типов, такие как [String]
или [UIView]
, нельзя отличать друг от друга, а не сравнивать их типы.
[UIView]
и [UIButton]
не имеют иерархии между собой, независимо от того, что UIButton
является подклассом UIView
. Вот почему даже если верно следующее:
Valuable.self is Any.Type // true
следующие приведения приводят к ошибкам по той же причине:
var anyArray: [Any] = ["cadena"]
anyArray as! [String] // BAD_INSTRUCTION error
"some string" as! Double // BAD_INSTRUCTION error
Эти две кладки не имеют никакого отношения, и отливка невозможна, так как as!
является принудительным броском, что приводит к ошибке.
Ответ 2
Я немного раскошу, и вам нужно добавить атрибут @objc следующим образом
@objc
protocol Valuable {
func value() -> Int
}
class Value: Valuable {
private let v: Int
init(value: Int) {
v = value
}
@objc func value() -> Int {
return v
}
}
let any: AnyObject = [Value(value: 1), Value(value: 2), Value(value: 3)]
let arrayOfValueable = any as! [Valuable] // [{v 1}, {v 2}, {v 3}]
Для получения дополнительной информации и получения anwser для "почему?": fooobar.com/questions/355208/...
Надеюсь, это поможет вам.
Изменить
Плюс это работает, только если вы используете AnyObject вместо Any: (
Ответ 3
Это работает для меня:
let arrayOfValuable = arrayOfValue?.map { $0 as Valuable }
или то же самое:
let arrayOfValuable2 = (any as? [Value])?.map { $0 as Valuable }
В заключение, arrayOfValuable
должен иметь тип [Valuable]?
Edit:
Или попробуйте следующее:
let arrayOfAny: [Any] = [Value(value: 1), Value(value: 2), Value(value: 3)]
let arrayOfValuable3 = arrayOfAny.map { $0 as Valuable }
Конечно, лучший способ сделать это - объявить arrayOfAny
как [Valuable]
, после чего у вас не будет проблем.
Ответ 4
Массив каста [Any] в массив [String]
let arrayOfAny:Array<Any> = [1,"2",3.0,CGFloat(4)]
let stringArray:Array<String> = arrayOfAny.map {String($0)}
print(stringArray)//"1", "2", "3.0","4.0"
Вывод:
Иногда его полезно преобразовать из одного типа массива в другой. Наилучший подход в большинстве случаев заключается не в преобразовании типа массива, а в том, чтобы выполнить проверку экземпляра перед упаковкой массива или проверки экземпляра после распаковки массива