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

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

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 改訂版が便利です。

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

follow us in feedly

Feedlyで最新記事を購読

Twitterで更新情報をゲット!

LINEでご感想・ご要望お送りください!
(スマホでLINEを起動 > 友だち追加 > QRコード)

関連記事

20140724-103334.jpg

iPhoneアプリ開発にオススメの本10選(2014年7月版)

@akio0911です。2013年12月に「レベル・目的別!iPhoneアプリ開発おすすめ本

記事を読む

20150410-122409.jpg

複数バージョンのXcodeを共存してMacにインストールする方法

いよいよXcode 6.3が正式にリリースされましたね。 今回は複数バージョンのXco

記事を読む

Auto LayoutやStoryboardの仕組みをしっかり理解できる!「UIKit徹底解説」

インプレスジャパン様より献本御礼。UIKit周りを中心に、「Auto Layout」「Story

記事を読む

20140916-130550

Swiftの列挙型、switch文、網羅性チェックが素晴らしい!

アップルの新プログラミング言語「Swift」をちょっとずついじってるんですが、列挙型とswitch文

記事を読む

20150108-113233.jpg

【Swift】コードをコンパクトに記述できるオプショナルチェーン(optional chaining)の性質と活用例

最近はSwiftでコーディングすることが多い@akio0911です。今日はSwiftでコード

記事を読む

20160724-154909.jpg

デザインの素人がノンデザイナーズ・デザインブックを読んだら、デザインの原則が結構分かるようになった!

ずっとエンジニアとして働いてきてデザインに関してはまったくの素人な僕ですが、「ノンデザイナーズ・

記事を読む

I20150808-104713.jpg

【Xcode】シミュレーターリストの表示がおかしくなった時の解決方法

Xcodeのシミュレーターリストの表示が、上のスクリーンショットのようにおかしくなってしまっ

記事を読む

I20160130-110811.jpg

【解決】Redundant conformance of ‘NSDate’ to protocol ‘Comparable’エラーが出た時の対処方法

アプリ開発中に「Redundant conformance of 'NSDate' to pro

記事を読む

I20150714-010114.jpg

【Swift】Objective-Cで書いたクラスをメソッド単位で少しずつSwiftへ移行する方法

Objective-Cで作った既存のアプリをSwiftで書き直したいと思っているのですが、一

記事を読む

iPhoneゲームを簡単に開発したいならこの本がオススメ!「Sprite Kit iPhone 2Dゲームプログラミング」

iPhoneゲームアプリを簡単に開発したいなら「Sprite Kit iPhone 2Dゲームプ

記事を読む

【Mac】macOSのメール(Mail.app)でGmailをサクサク快適に使う方法

macOSに搭載されているメールアプリ、いわゆるMail.ap

【メンズネイル】新宿区大久保のネイルサロンで紫とピンクのネイルにしてもらった

2016年11月12日、東京都新宿区大久保にあるネイルサロン「

[Xcode][Swift]ボタンなどの同時タップを禁止する

iOSで、ボタンなどの同時タップを禁止する方法を紹介します。

【HomeKit】家の照明をSiriで制御できるようにしてみた

Apple製品と家電を連携するシステム「HomeKit(ホーム

【メンズネイル】新宿区大久保のサロンでターコイズとピンクのメタルネイルにしてもらった

2016年10月22日、いつもお世話になっているネイルサロン「

→もっと見る

PAGE TOP ↑