web-dev-qa-db-ja.com

Swift:Anyをプロトコルオブジェクトの配列にキャストする

プロトコルがあります:

protocol Valuable {
    func value() -> Int
}

プロトコルを実装するクラス:

class Value: Valuable {
    private let v: Int

    init(value: Int) {
        v = value
    }

    func value() -> Int {
        return v
    }
}

Any型の変数に格納されたValueオブジェクトの配列があります。

let any: Any = [Value(value: 1), Value(value: 2), Value(value: 3)]

Anyを[Value]にキャストできます:

let arrayOfValue = any as? [Value] // [1, 2, 3]

Anyを[Valuable]にケース分けできないのはなぜですか?

let arrayOfValuable = any as! [Valuable] // compiler error BAD INSTRUCTION
let arrayOfValuable2 = any as? [Valuable] // nil
15
Andrii H.

更新: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]のように、互いにキャストしたり、型を比較したりすることはできません。

UIButtonUIViewのサブクラスであるかどうかに関係なく、[UIView][UIButton]は相互に階層を持ちません。これが、以下がtrueを返す場合でも、その理由です。

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つのクラスには関係がなく、キャストは不可能です。

6
LopSae

私はいくつかのDigを行い、次のように@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}]

詳細と「なぜ?」のアンサーを取得するには: https://stackoverflow.com/a/25589323/989631

これがお役に立てば幸いです。

編集する

さらに、Any :(の代わりにAnyObjectを使用する場合にのみ機能します。

3
Błażej

わたしにはできる:

let arrayOfValuable = arrayOfValue?.map { $0 as Valuable }

または同じ:

let arrayOfValuable2 = (any as? [Value])?.map { $0 as Valuable }

結論として、arrayOfValuableのタイプは[Valuable]?である必要があります

編集:

またはこれを試してください:

let arrayOfAny: [Any] = [Value(value: 1), Value(value: 2), Value(value: 3)]
let arrayOfValuable3 = arrayOfAny.map { $0 as Valuable }

確かに、そのためのより良い方法は、arrayOfAny[Valuable]として宣言することです。これにより、後で問題が発生することはありません。

2
Beraliv

Array [Any]からArray [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"

結論:
1つの配列型から別の配列型に変換すると便利な場合があります。最善の方法は、ほとんどの場合、配列タイプを変換せず、配列をパックする前にインスタンスチェックを実行するか、配列をアンパックした後にインスタンスチェックを実行することです。

0
eonist