#はじめに
話的には前回の続きですが、この記事だけでも理解はできるように書いています。Swiftのプロトコルについて、もう少し掘り下げて書いていきます。
#Swiftのプロトコル
以下に、プロトコルの実装を書きます。
protocol Animal {
func cry()
}
これをクラスに準拠させることで、Animalプロトコルを持つクラスはcry()メソッドが実装されていることが約束されます。
class Dog: Animal {
func cry() {
print("wan!")
}
}
#オプショナルなメソッド
ここで、ある人がこう考えたとします。
「Animalプロトコルの中に、walk()メソッドも実装したいな。
あ、でも魚とかは歩かないか。なら、実装を必須にはしない形で宣言できないかな。。」
##Objective-Cのプロトコルではできる
プロトコルの概念はSwiftの前身であるObjective-Cの頃からありました。そしてObjective-Cのプロトコルでは実装が必須ではないメソッドも簡単に宣言することができます。以下に、Objective-Cのプロトコルの実装を書きます。
@protocol Animal
@required // 実装必須なメソッド (@requiredは省略可能)
- (void) cry;
@optional // 実装が必須ではない、実装しなくてもいいメソッド
- (void) walk;
@end
このように、@optionalをつけるだけで宣言ができました。
##Swiftのプロトコルではできない
しかし、同様の考え方でswiftでも実装しようとするとうまくいきません。
protocol Animal {
func cry()
optional func walk()
} //コンパイルエラー
上のコードの場合はコンパイルするとエラーになります。それをコンパイラの言うように訂正していくと、下のコードのようになるかと思います。
@objc protocol Animal {
func cry()
@objc optional func walk()
}
なんだかよくわからない、@objcというものが出てきました。これはプロトコルをObjective-Cで宣言されたものとして扱ってください、とコンパイラに伝えるために使われます。つまり、Swiftのみのコードでオプショナルなメソッドは存在せず、宣言することができません。
##@objcをつけることの弊害
@objcをつけたとしてもオプショナルなメソッドを実装できたのであればいい、と考える人もいますが、極力避けたほうがいいと私は考えます。@objcをつけることで、swiftのプロトコルでできていた以下のようなことができなくなります。
- プロトコルを構造体や列挙型に準拠させられなくなる。
- プロトコル内でジェネリクスが使えなくなる。
これらの弊害は、Swiftの思想に反するもので後々に大きな弊害となる可能性があります。SwiftUIでは構造体で様々なViewを宣言するので、今からこういう使い方はしないように習慣づけておいて方がよいでしょう。
#オプショナルなメソッドを、Delegateで利用
@objcをつければオプショナルなメソッドを一応定義することができることがわかりました。では、このAnimalプロトコルを準拠したクラスをdelegateで利用するとどうなるのでしょうか。以下に、コードで示します。
@objc protocol Animal {
@objc optional func cry() -> String
}
class Dog: Animal {
func cry() -> String {
return "wan!"
}
}
let delegate: Animal = Dog()
print(delegate.cry?()) // 出力結果:Optional("wan!")
このようにメソッドの返り値はString型でオプショナルではありませんが、メソッド自体がオプショナルであるため返り値もオプショナルになります。クラス内のメソッドだけを見ているとオプショナルということがわかりませんし、オプショナルを外す処理が後に必要になりますし、あまりきれいな書き方ではありませんね。このことからもなるべくであれば使いたくない書き方であることがわかるかと思います。
#Swiftの思想
なぜこのような仕様になっているかと言うと、Swiftの言語思想的にプロトコルで実装が任意のメソッドは存在しないと考えられているからです。上記の例ではwalkを実装する時点で、本当にAnimalプロトコルという名前で正しかったのでしょうか。そしてこのプロトコルはFishクラスにも準拠させる必要があったのでしょうか。というような点を考える必要があったと思います。(Fishクラスには専用のFishプロトコルを用意する、FishプロトコルとAnimalプロトコルの内容がかぶるところが多いようであれば、その部分を抜き出して親プロトコルを作る、など)
#まとめ
Objective-CからiOS開発をしている人からすると、一度はプロトコル内にオプショナルなメソッドを宣言しようとして詰まった部分だと思います。しかし、Xcodeが修正方法を提案してくれるので詳しくは調べずに@objcをつけてやり過ごした人もいるのではないでしょうか。~~かくいう私もその一人です。既存のプロトコルを準拠させたいけど新しくメソッドを実装するのはめんどくさい、という理由でメソッドをオプショナルにしそうになったこともあります。~~そういう人たちが、安易に@objcでやり過ごさずに済み、後に負債を残さない助けに成れれば幸いです。