29116
Swiftでif文にArrayの要素を使用できません。  
以前Xcode8にアップデートした際にConvertで元のコードが変更されたのですが、その際に出てきたエラーです。  
エラー内容は

```

    Could not cast value of type '_SwiftTypePreservingNSNumber' (0x1046d7698) to 'NSDecimalNumber' (0x101a400e8).
    
```

です。

どのようにすれば解決できるでしょうか？

コードはこちらになります。

AppDelegate

```

     var Array:NSMutableArray?
    
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    
        Array = [0,0,0,0,0,0]
    
    }
    
```

ViewController.h

let appDelegate:AppDelegate = UIApplication.shared.delegate as! AppDelegate
//AppDelegateのインスタンスを取得

```

    if((appDelegate.Array![2] as! NSObject) as! Decimal  == 1){
        aP += 0.5
        bP += 0.5
    }else if((appDelegate.Array![2] as! NSObject) as! Decimal == 2 ){
        cP += 0.5
        dP += 0.5
    }
    
```
CC BY-SA 3.0
2016-09-25T08:56:24.333
29113
2016-09-25T10:16:23.940
2016-09-25T09:24:43.823
12886
12886
question
swift ios xcode iphone
Swiftでif文の条件文にArrayの要素を使用できません
`Array`と言うのはSwiftの標準ライブラリのデータ型(の中でも最もよく使われるもの)と全く同じ名前ですので、自分のアプリの中で別の意味で使うのは避けた方が良いと思います。

またSwiftがらみのAppleのコーディングルールでは、「型名のみに大文字のキャメルケースを用いて、それ以外の場合は小文字で始める」と言うのがあり、Appleのサンプルコード群の中でも、本家StackOverflowに名回答を連発している古参Swiftプログラマーの間でも大変よく守られています。その辺のルールはきちんと守った方がコードの読みやすさを格段に増しますので、今後は少し気にしてコーディングされた方が良いでしょう。

以下の説明では勝手に`array`と変更させていただきます。

* * *

さて、本題の方ですが、Swift
3になって、様々な自動の型変換が廃止された上に、`NSArray`や`NSDictionary`が`[Any]`や`[AnyHashable:
Any]`と言うデータ型としてSwift側にimportされるようになりました。この`Any`と言うのは大変扱いにくいもので、「Swift
3になってバグが出た」と称する報告のかなりの部分がこの`Any`に起因しています。

今後作られるSwift 3のアプリでは、 **`NSArray`, `NSMutableArray`, `NSDictionary`,
`NSMutableDictionary`などのObjective-C由来のデータ型は特別な理由がない限り使用せず、`Array`,
`Dictionary`と言ったSwift由来のデータ型を使用する**ようにした方がコードがシンプルでわかりやすくなります。

(残念ながら「Swift2用に」書かれたサイトでも特別な理由がないまま`NSArray`や`NSDictionary`を多用するコードを掲載していることがあります。そのような場合には自分で頑張って変換してください。)

あなたの`AppDelegate`はこんな感じになるでしょう。

```

    var array: [Int] = [] //整数しか格納しないようなので、`[Int]` (`Array<Int>`と同じ意味)と宣言しておく
    
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    
        array = [0,0,0,0,0,0] //使い方は`NSMutableArray`と大きく変わらないが、時々細かい修正が必要になる
    
        return true
    }
    
```

ViewControllerの方はこんな感じになります。

```

        let appDelegate = UIApplication.shared.delegate as! AppDelegate //AppDelegateのインスタンスを取得
    
        if appDelegate.array[2] == 1 {
            aP += 0.5
            bP += 0.5
        } else if appDelegate.array[2] == 2 {
            cP += 0.5
            dP += 0.5
        }
    
```

`Array`型をOptionalにする必要性は薄いと思われるので、`AppDelegate`の方で非Optionalに変更しておきましたから、危険な「私のアプリをクラッシュさせてね演算子」`!`を使う必要もありませんし、要素のデータ型は`Int`と宣言してありますから、一切のキャストは必要なくなります。

実際のアプリでは整数以外のデータもその配列に保存したい、と言う場合があるのかもしれませんが、その場合にはそれ専用の別の配列を用意するか、整数と整数以外のデータをまとめて持てるような自分専用のデータ型を定義した方がわかりやすいコードになるでしょう。(良い参考記事等が見つからなければまた別件としてご質問ください。)

* * *

ここからはエラーコードの詳細ですが、特に **Swift 3の内部でどんなことが起こっているのか詳しく知りたいという人以外は読まなくても大丈夫** です。

整数リテラル(`0`)を含む配列リテラル(`[0,0,0,0,0,0]`)を`NSMutableArray`型の変数に代入しようとすると、Swiftは各整数リテラルの値を`NSNumber`型のデータに変換しようとします。`NSNumber`型というのは「クラスクラスター」と呼ばれるもので、実際の`NSNumber`型のインスタンスは内緒のサブクラスのどれかなのですが、ご質問者の例の場合、それがエラーメッセージにある`_SwiftTypePreservingNSNumber`です。何も情報が公開されていないので、「`NSNumber`の一種だ」とだけ思っていれば大丈夫です。

一方元コードの`ViewController`では、それを`NSObject`経由で`Decimal`型に変換しようとしています。上記の`_SwiftTypePreservingNSNumber`は`NSNumber`のサブクラスであり、当然それは`NSObject`のサブクラスでもありますから、その部分はパスするのですが、参照型である`NSObject`を値型である`Decimal`に変換するには、その実際の中身は、参照型であり`NSObject`のサブクラスでもある`NSDecimalNumber`でないといけません。(`NSObject`を`String`に変換する場合、実際の中身は`NSString`でないといけないと言うのと同じ理屈です。)

これで元のエラーメッセージを読み解く準備は終わりです。「(`NSMutableArray`に入っていた)`_SwiftTypePreservingNSNumber`と言うデータ型(`0`が変換されてその中に入ってます)は(`Decimal`型にキャストするのに必要な)`NSDecimalNumber`には変換できませんよ」と言っているわけです。

Swift 2までは、Swiftのデータ型とObjective-
Cの対応するデータ型との変換は大抵自動的に行われていたので、ほとんど気にする必要がない場合が多かったのですが、それらの自動変換のほとんどはSwift
3で削除されてしまったので、意識していかないと実行時にしかエラーを見つけられない厄介なバグを混入してしまうこともあります。しっかり覚えていってください。
CC BY-SA 3.0
2016-09-25T10:16:23.940
29116
2016-09-25T10:16:23.940
13972
29113
answer