3分でわかるSwiftのキャストの基本

Swiftでのキャストについて、調べましたのでブログにまとめておきます。
環境:Xcode6 beta6

詳細は、”The Swift Programming Language””Type Casting”に記載されています。

検証は以下のクラスを用いて行いました。

protocol SomeProtocol {
    func protocolMethod()
}

class BaseClass : SomeProtocol{
    func protocolMethod() {
        println("Protocol Method BaseClass")
    }
}

class SomeClass1 : BaseClass {
    func some1() {
        println("SomeClass1")
    }
}

class SomeClass2 : BaseClass {
    func some2() {
        println("SomeClass2")
    }
}

class OtherClass : SomeProtocol{
    func protocolMethod() {
        println("Protocol Method OtherClass")
    }
}

型チェック

Swiftでの型チェックはis演算子で行います。

var baseObjects:[AnyObject] = [
    SomeClass1(),
    SomeClass1(),
    SomeClass2(),
]

for obj in baseObjects {
    if obj is SomeClass1 {
        println("SomeClass1")
    }
    if obj is SomeClass2 {
        println("SomeClass2")
    }
}

以下のように、チェック対象の変数の型が明らかな場合、Xcodeによる文法チェックでエラーが表示されます。

var intValue = 1
if intValue is Int { <-- ここでエラーが出る
    println("intValue is Int")
}
var intValue:Int = 1
if intValue is Int { <-- ここでエラーが出る
    println("intValue is Int")
}

ダウンキャスト

Swiftでのダウンキャストはas演算子で行います。

var baseObjects:[BaseClass] = [
    SomeClass1(),
    SomeClass1(),
    SomeClass1(),
]

for obj in baseObjects {
    let some1 = obj as SomeClass1
    some1.some1()
}

配列に格納されている型が同一である事がわかっていれば、以下のように短縮して記述できます。

var baseObjects:[BaseClass] = [
    SomeClass1(),
    SomeClass1(),
    SomeClass1(),
]

for obj in baseObjects as [SomeClass1] {
    obj.some1()
}

ダウンキャストが可能かどうか不明な場合、as?演算子で行います。

var baseObjects:[BaseClass] = [
    SomeClass1(),
    SomeClass1(),
    SomeClass2(),
]

for obj in baseObjects {
    if let some1 = obj as? SomeClass1 {
        some1.some1()
    }
    if let some2 = obj as? SomeClass2 {
        some2.some2()
    }
}

これがハマるポイントになるかどうかわかりませんが、上記の検証をしていた中で気づいた事を記載します。

Swiftの型推論はArray型にも適用されます。
その為、配列の型を指定しなかった場合、以下のような挙動になります。
・SomeClass1、SomeClass2の共通の親クラスであるBaseClass型になる。

var array = [
    SomeClass1(),
    SomeClass2()
]

・配列に格納されているクラスに共通のクラスがないため、本来であればエラーになるのですが、FoundationフレームワークがimportされているとNSArrayとして扱われます。
(自動的にAnyObjectになってくれたらいいのに・・・)

var array = [
    SomeClass1(),
    SomeClass2(),
    OtherClass()
]

このNSArray型に、Swiftのオブジェクトを格納していると、以下のコードでハングします。
(内部で methodSignatureForSelector がコールされているようで、そこでハングします)

println(\(array))

今のところ、下記いずれかの方法で回避できることを確認しています。

 

  • NSArray型に格納するオブジェクトをNSObjectを継承したクラスにする
  • 配列をAnyもしくはAnyObjectで宣言する

なお、上記の配列に格納されているオブジェクトはSomeProtocolを実装しています。
配列をSomeProtocol型にしたい場合は、以下のように記述します。

var array:[SomeProtocol] = [
    SomeClass1(),
    SomeClass2(),
    OtherClass()
]

最後は配列の話になりましたが、この辺のノウハウが蓄積される事を願うばかりです。

この記事を書いた人

なかの
なかの
アプリ開発チームで、iOSエンジニアやってます。         *'``・* 。         |     `*。        ,。∩      *           + (´・ω・`) *。+゚       `*。 ヽ、  つ *゚*        `・+。*・' ゚⊃ +゚        ☆   ∪~ 。*゚         `・+。*・ ゚
Pocket