まーぽんって誰がつけたの?

iOS→Scala→インフラなおじさん技術メモ

SwiftでisKindOfClass:するにはダウンキャストを利用する

Objective-CでisKindOfClass

Objective-Cだとid型で何かを受け取ることがあって、それを検査するのにisKindOfClass:使いますね。例えばこんな感じ。

- (IBAction)buttonDidPush:(id)sender
{
    if ([sender isKindOfClass:[UIButton class]])
    {
        NSLog(@"This is UIButton");
    }
    else if ([sender isKindOfClass:[UIBarButtonItem class]])
    {
        NSLog(@"This is UIBarButonItem");
    }
}

Objective-Cで、[UIButton class]って書くんだから、SwiftならUIButton.classだろうと思ってそう書いてもコンパイルエラーになってしまいます。

Swiftでclassを表現するには

じゃあ、どうするかって話なんですけど普通にそのままクラス名を書くだけでよかった。うーん、なんかこの辺ちゃんと理解できてないけど慣れるしかないっすね。

@IBAction func buttonDidPush(sender: AnyObject) {
    if sender.isKindOfClass(UIButton) {
        println("This is UIButton")
    } else if sender.isKindOfClass(UIBarButtonItem) {
        println("This is UIBarButton")
    }
}

ちょっと話は逸れてしまったけどSwiftでisKindOfClassは使える。でも、Swiftにはもっと正しい方法があります。

Swiftで型を検査する正しい方法

is演算子がある。うん。超簡単。

@IBAction func buttonDidPush(sender: AnyObject) {
    if sender is UIButton {
        println("This is UIButton")
    } else if sender is UIBarButtonItem {
        println("This is UIBarButton")
    }
}

型を検査したあとオブジェクトを操作したい場合

ここで、タイトルの話がはじめて出てきます。

まぁたいてい、isKindOfClassが使いたいときってその型特有のプロパティを取りたいってときです。Swiftだとこういう風に書けます。

@IBAction func buttonDidPush(sender: AnyObject) {
    if let button = sender as? UIButton {
        println("This is UIButton \(button.titleLabel?.text)")
    } else if let barButton = sender as? UIBarButtonItem {
        println("This is UIBarButton \(barButton.title)")
    }
}

ここで難しかったのは、as?ってところ。

最初見たときはなんだこれって思ったけど、そもそも、asっていうのはsenderというAnyObjectUIButtonにキャストするってことです。 Objective-Cのときはこの辺りはゆるくて別にUIButtonにキャストできなくてもクラッシュしたりはしなかった。でもSwiftの場合はキャストできない場合はクラッシュしてしまいます。

そこで安全にキャストするためにas?という構文が使えて、これはキャストできない場合はnilを返してくれます。それを利用すれば、上記のように、let button = sender as? UIButtonみたいに書くことができるというわけです。これならnilのときは安全に評価されてif文の中に入らないだけですみますね。