Swiftでプロトコル型に対して===演算子を使いたい時の対処法

公開日: : 最終更新日:2016/02/10 iOSアプリ開発

記事内に広告を含む場合があります。記事内で紹介する商品を購入することで、当サイトに売り上げの一部が還元されることがあります。

20160204-120840.jpg

プロトコル型の変数や定数に対して===演算子を使うと「error: binary operator ‘===’ cannot be applied to two ‘プロトコル名’ operands」というエラーが出ることがありますが、その時の対処法について解説したいと思います。

    

「===演算子」とは?

以下、詳解 Swift 改訂版からの引用です。

まだ説明のない演算子に「===」と「!==」があります。これは参照型の値であるクラスのインスタンスの実体が同一のものかどうかを調べるためのものです。

Swiftには、複数箇所から参照されているインスタンスが同一のものかどうかを調べるための演算子として「===」が用意されています。この演算子はインスタンスの値が等しいかどうか(同値性)ではなく、メモリ上にある同じインスタンスの実体を指しているかどうか(同一性)を調べます。

同一ではないことを調べる演算子は「!==」です。

    

コードを書いて確認してみましょう。

// Swiftにおいて、Stringはstruct(値型)、NSStringはclass(参照型)
let str1 = NSString(string: "hello")
let str2 = NSString(string: "hello")
let str3 = str1

str1 == str2 // true

str1 === str2 // false

str1 == str3 // true

str1 === str3 // true

str1とstr2は、文字の内容は同じですが、別インスタンスとなっています。

また、str3はstr1と同じインスタンスを参照しています。

    

プロトコル型の変数や定数に対して===演算子を使ってみる

では、プロトコル型の変数や定数に対して===演算子を使ってみましょう。

protocol PersonalProtocol {
    var name : String { get set }
}

class Person : PersonalProtocol {
    var name : String = ""
}

let personal1 : PersonalProtocol = Person()
let personal2 : PersonalProtocol = Person()

// error: binary operator '===' cannot be applied to two 'PersonalProtocol' operands
personal1 === personal2

===演算子を使っているところでエラーが出ています。

    

===演算子の定義について調べてみる

===演算子の定義について確認してみましょう。

public func ===(lhs: AnyObject?, rhs: AnyObject?) -> Bool

引数として、2つのAnyObject?型を受け取っていますね。

以下、詳解 Swift 改訂版からの引用です。

継承関係がないクラスのインスタンス同士を何らかの都合で1つの型で表したい場合もあります。そのような場合、SwiftではAnyObjectという型を利用できます。

    

AnyObjectの定義についても調べてみましょう。

/// The protocol to which all classes implicitly conform.
@objc public protocol AnyObject {

「すべてのクラスが暗黙的に適合するプロトコル」と書かれていますね。

    

普通のプロトコル型の変数や定数にはstructのインスタンスも代入できるから、===演算子が使えない

以上のことを踏まえると、普通のプロトコル型の変数や定数にはstructのインスタンスも代入できるので、2つのAnyObject型の引数を受け取る===演算子をプロトコル型の変数や定数に対して使用することはできないということですね。

以下、実例を挙げてみます。

protocol PersonalProtocol {
    var name : String { get set }
}

class PersonClass : PersonalProtocol {
    var name : String = ""
}

struct PersonStruct : PersonalProtocol {
    var name : String = ""
}

let personal1 : PersonalProtocol = PersonClass()
let personal2 : PersonalProtocol = PersonStruct()

// error: binary operator '===' cannot be applied to two 'PersonalProtocol' operands
personal1 === personal2

    

structをAnyObjectに適合させてみる

では、structのほうをAnyObjectに適合させてみてはどうでしょうか?

実際にやってみます。

protocol PersonalProtocol {
    var name : String { get set }
}

class PersonClass : PersonalProtocol {
    var name : String = ""
}

// error: non-class type 'PersonStruct' cannot conform to class protocol 'AnyObject'
struct PersonStruct : PersonalProtocol, AnyObject {
    var name : String = ""
}

let personal1 : PersonalProtocol = PersonClass()
let personal2 : PersonalProtocol = PersonStruct()

// error: binary operator '===' cannot be applied to two 'PersonalProtocol' operands
personal1 === personal2

「error: non-class type ‘PersonStruct’ cannot conform to class protocol ‘AnyObject’」というエラーが出てしまいました。クラスではない型をAnyObjectプロトコルに適合させることはできないみたいです。

    

プロトコルをAnyObjectから継承させてみる

では、プロトコルをAnyObjectから継承させてみましょう。

protocol PersonalProtocol : AnyObject {
    var name : String { get set }
}

class PersonClass : PersonalProtocol {
    var name : String = ""
}

let personal1 : PersonalProtocol = PersonClass()
let personal2 : PersonalProtocol = PersonClass()

personal1 === personal2 // false

これでプロトコル型に対して===演算子を使えるようになりました。

なお、PersonalProtorolはAnyObjectを継承しているので、struct型をPersonalProtorolに適合させることはできなくなります。

protocol PersonalProtocol : AnyObject {
    var name : String { get set }
}

class PersonClass : PersonalProtocol {
    var name : String = ""
}

// error: non-class type 'PersonStruct' cannot conform to class protocol 'AnyObject'
struct PersonStruct : PersonalProtocol {
    var name : String = ""
}

    

プロトコルにclassキーワードを記述してもOK

また別の方法として、プロトコルにclassキーワードを記述してもOKみたいです。

以下、詳解 Swift 改訂版からの引用です。

プロトコルはクラス、構造体、列挙型で採用することができますが、値型よりも参照型のデータでの実装を前提としている場合や、継承を利用する場合など、クラスでの実装だけを想定してプロトコルを定義する場合があります。そのような場合、クラスだけが対象であることを明示するために、プロトコル名に続いてコロン「:」を置き、キーワードとしてclassと記述します。

実際にやってみましょう。

protocol PersonalProtocol : class {
    var name : String { get set }
}

class PersonClass : PersonalProtocol {
    var name : String = ""
}

let personal1 : PersonalProtocol = PersonClass()
let personal2 : PersonalProtocol = PersonClass()

personal1 === personal2 // false

===演算子が使えてますね。

    

ちなみにclassキーワードを付けたプロトコルは、AnyObjectプロトコルを継承している扱いになるようです。

protocol PersonalProtocol : class {
    var name : String { get set }
}

class PersonClass : PersonalProtocol {
    var name : String = ""
}

let personal1 : PersonalProtocol = PersonClass()

personal1 is AnyObject // true

    

@akio0911はこう思った。

エラーが出た時は「なんでプロトコルに===演算子が使えないの!?」と思ったのですが、いろいろと調べてみるときちんと筋の通った仕組みになっていることが分かりますね。

    

Swiftの細かい仕様について学ぶには、詳解 Swift 改訂版が便利です。

この記事を書いた人
あきお(@akio0911
派手髪iOSエンジニア。Twitterアカウントは@akio0911。YouTubeチャンネル「あきおチャンネル」にてiOSアプリ開発講座を公開中。著書に「iPhoneアプリ開発レシピ」「cocos2d for iPhoneレッスンノート」など。iOSアプリ開発をテーマとしたオンラインサロン「アプリ道場サロン」を運営。東京・大阪にてアプリ開発講座「アプリクリエイター道場」を主催。

関連記事

この記事が気に入ったら「いいね!」しよう

Twitterで更新情報をゲット!

PAGE TOP ↑