プロトコルの型チェックまわりでつまづいたことメモ

こんにちワッフル #挨拶


さっそくですがだめサンプル

突然ですが、以下のコードをご覧ください。これ何故か動かないんですよ。

public protocol Shape {
    func draw()
}

public class Triangle : Shape {
    public func draw() {
        println("三角形!")
    }
}

public class Rectangle : Shape {
    public func draw() {
        println("長方形!")
    }
}


// インスタンス化します
let triangle : Shape = Triangle()
let rectangle : Shape = Rectangle()

// Shape型の配列に入れます
let shapes : [Shape] = [triangle, rectangle]

// shapesの要素に、triangleが含まれているか
// 以下のコードはコンパイルエラーになります
for shape : Shape in shapes {
    if triangle === shape {
        shape.draw()
    }
}

コンパイルすると、以下のエラーが出てしまいます。

Type 'Shape' does not conform to protocol 'AnyObject'

どうやら Swift の場合、コンパイル時に内部的な型チェックが走っていて(なんでやねん)、しかもそれが上記のコードではうまく働いていないようです*1

困り果てて“The Swift Programming Language”, Apple Inc., Aug 4, 2014. 版*2 を読み直していたら、こんな記載を 今さら 発見。

@objc protocol HasArea {
    var area: Double { get }
}

You can check for protocol conformance only if your protocol is marked with the @objc attribute, as seen for the HasArea protocol above. This attribute indicates that the protocol should be exposed to Objective-C code and is described in Using Swift with Cocoa and Objective-C. Even if you are not interoperating with Objective-C, you need to mark your protocols with the @objc attribute if you want to be able to check for protocol conformance.

型チェックしたいプロトコルは、宣言時に @objc 属性を明示してくださいね、べつに Objective-C コードを混ぜ書きしたいわけでない場合でも、この属性は必要なんだよ、という意味だと思います。よくわからないですが。

というわけで、上記の Shape プロトコルを以下のように修正したら、無事コンパイルが通るようになりました。

@objc public protocol Shape {
    func draw()
}

めでたし、めでたし。

*1:Swift はもともとそういう仕様で、結局、当初のコードの書き方に問題があったわけですが。

*2:頻繁に改訂されるので、一応いつの時点の情報かを書いておきます。

Copyright (c) 2012 @tercel_s, @iTercel, @pi_cro_s.