はじめに
こんにちはsuzukiです。久しぶりにSwiftの記事を投稿します。
アーキテクチャの都合上仕方ないのですが、プロトコルだらけのコードを読んでます。
プロトコルとは
手順書とか決まりごとのような表現がされていますが、概ねその通りだと思っています。
例えばIntやStringで比較を行う際に==
や<
を使います。==
や<
で比較できるのはIntとStringがComparableと言うプロトコルに準拠して作られているためです。
クラスの継承と似ているように思えますが異なります。
使用シーンの違いなどはこちらのサイトが参考になるかと思います。
- プロトコル
- 複数のプロトコルを設定できる
- 準拠する対象を意識せず設定できる
- 構造体や列挙型でも利用できる
- クラス継承
- 複数のクラスを継承できない
- 親クラスを継承する為親のクラスを意識する必要がある
- 構造体は継承できない
プロトコルの作成方法
プロトコルを作成するには下記のようなコードになります。
1 2 3 4 | // プロトコル名 protocolXXXXXXXX { //プロトコルが準拠する内容 } |
- プロトコルで宣言できる内容
- 関数
- インスタンスプロパティ・タイププロパティ
- イニシャライザ
- typealias
- 添字付け
面積を返す関数を持つ図形プロトコルを定義すると下記のようなコードになります。
1 2 3 | protocolFigure { funcculcArea()->Float } |
プロトコルを採用したクラス作成
作成したプロトコルを採用するには下記のように記述します。
1 2 3 | classTriangle: Figure{ } |
これだけだとTriangleクラスがFigureプロトコルに準拠していないということでエラーが発生します。Type 'Triangle' does not conform to protocol 'Figure'
Figureプロトコルでfunc culcArea() -> Float
という関数を宣言したので、プロトコルを採用したクラスでは宣言された関数を実装しなければなりません。
1 2 3 4 5 6 7 8 | classTriangle: Figure{ letbottom,height:Float //culcAreaを作成するとエラーが消える funcculcArea()->Float{ return bottom*height/2 }f } |
使用例
構造体に採用することも可能ですし、プロトコルを採用されていれば配列にまとめることも可能です。
試しに少しコードを書いてみました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | protocolFigure { funcculcArea()->Float } structSquare: Figure{ letbottom,height:Float //FigureProtocolを準拠する為のculcArea関数 funcculcArea()->Float{ returnbottom*height } } classTriangle: Figure{ letbottom,height:Float init(bottom:Float,height:Float){ self.bottom=bottom self.height=height } //FigureProtocolを準拠する為のculcArea関数 funcculcArea()->Float{ returnbottom*height/2 } } classCircle: Figure{ letradius:Float init(radius:Float){ self.radius=radius } //FigureProtocolを準拠する為のculcArea関数 funcculcArea()->Float{ returnradius*radius*3.14 } } //継承関係があるクラスに設定する場合は親クラスを左側に書きます classViewController:UIViewController,Figure{ overridefuncviewDidLoad(){ letsquare=Square(bottom:10,height:10) lettriangle=Triangle(bottom:10,height:10) letcircle=Circle(radius:10) letfigures:[Figure]=[square,triangle,circle] vartotalArea:Float=0 figures.enumerated().forEach{ totalArea+=$0.element.culcArea() } print(totalArea) } //FigureProtocolを準拠する為のculcArea関数 funcculcArea()->Float{ return0 } overridefuncdidReceiveMemoryWarning(){ super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } } |
ClassOnlyProtocolとは
前の章で構造体や列挙型にもプロトコルの使用が可能と書きましたが、構造体と列挙型等に制限を行いクラスのみで設定できるプロトコルを作成する事も可能です。
1 2 3 4 | //デリゲートの宣言 protocolFigureDelegate: AnyObject{ funcdelegateMethod(area:Float) } |
メリット
こちらを使うメリットとしては、参照を渡す場合に弱参照のweakを設定できるようになります。
デリゲートなどで相手のインスタンスを参照するような場合には弱参照で保持し循環参照を防ぐことができます。
AnyObjectを指定しない場合は'weak' must not be applied to non-class-bound 'FigureDelegate'; consider adding a protocol conformance that has a class bound
上記のようなエラーが表示されます。
エラー原因はそもそも弱参照は参照型に対してのみ定義できます。
プロトコルは参照型(クラス),値型(構造体、列挙型)全て設定できるため、weakを設定しているのに値型が入る可能性があります。
使用例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | //デリゲートの宣言 protocol FigureDelegate:AnyObject{ func delegateMethod(area:Float) } classTriangle{ let bottom,height:Float vardelegate:FigureDelegate? init(bottom:Float,height:Float){ self.bottom=bottom self.height=height } func callDelegate(){ //delegateMethodの呼び出し delegate?.delegateMethod(area:bottom *height/2) } } classViewController:UIViewController,FigureDelegate{ override func viewDidLoad(){ let triangle=Triangle(bottom:10,height:10) triangle.delegate=self triangle.callDelegate() } //デリゲートメソッドの設定 func delegateMethod(area:Float){ print(area) } override func didReceiveMemoryWarning(){ super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } } |
最後に
プロトコルの設定方法をまとめさせていただきました。
プロトコルの拡張は複雑になっているため、また今度改めてまとめられたらと思っております。