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"

Вывод:
Иногда его полезно преобразовать из одного типа массива в другой. Наилучший подход в большинстве случаев заключается не в преобразовании типа массива, а в том, чтобы выполнить проверку экземпляра перед упаковкой массива или проверки экземпляра после распаковки массива