カテゴリー: iOS

[Swift] プロトコルを弱参照するために

はじめに

こんにちはsuzukiです。久しぶりにSwiftの記事を投稿します。
アーキテクチャの都合上仕方ないのですが、プロトコルだらけのコードを読んでます。

プロトコルとは

手順書とか決まりごとのような表現がされていますが、概ねその通りだと思っています。
例えばIntやStringで比較を行う際に== を使います。
== で比較できるのはIntとStringがComparableと言うプロトコルに準拠して作られているためです。
クラスの継承と似ているように思えますが異なります。
使用シーンの違いなどはこちらのサイトが参考になるかと思います。

    プロトコル
  • 複数のプロトコルを設定できる
  • 準拠する対象を意識せず設定できる
  • 構造体や列挙型でも利用できる
    クラス継承
  • 複数のクラスを継承できない
  • 親クラスを継承する為親のクラスを意識する必要がある
  • 構造体は継承できない

プロトコルの作成方法

プロトコルを作成するには下記のようなコードになります。

// プロトコル名
protocol XXXXXXXX  {
    //プロトコルが準拠する内容
}
    プロトコルで宣言できる内容
  • 関数
  • インスタンスプロパティ・タイププロパティ
  • イニシャライザ
  • typealias
  • 添字付け

面積を返す関数を持つ図形プロトコルを定義すると下記のようなコードになります。

protocol Figure  {
    func culcArea() -> Float
}

プロトコルを採用したクラス作成

作成したプロトコルを採用するには下記のように記述します。

class Triangle: Figure {

}

これだけだとTriangleクラスがFigureプロトコルに準拠していないということでエラーが発生します。
Type 'Triangle' does not conform to protocol 'Figure'
Figureプロトコルでfunc culcArea() -> Floatという関数を宣言したので、プロトコルを採用したクラスでは宣言された関数を実装しなければなりません。

class Triangle: Figure {
    let bottom,height: Float

    //culcAreaを作成するとエラーが消える
    func culcArea() -> Float {
        return  bottom * height / 2
    }f
}

使用例

構造体に採用することも可能ですし、プロトコルを採用されていれば配列にまとめることも可能です。
試しに少しコードを書いてみました。

protocol Figure  {
    func culcArea() -> Float
}
struct Square: Figure {
    let bottom,height: Float

    //FigureProtocolを準拠する為のculcArea関数
    func culcArea() -> Float {
       return bottom * height
    }
}
class Triangle: Figure {
    let bottom,height: Float
    
    init(bottom: Float , height: Float) {
        self.bottom = bottom
        self.height = height
    }

    //FigureProtocolを準拠する為のculcArea関数
    func culcArea() -> Float {
        return bottom * height / 2
    }
}
class Circle: Figure {
    let radius: Float
    
    init(radius: Float) {
        self.radius = radius
    }

    //FigureProtocolを準拠する為のculcArea関数
    func culcArea() -> Float {
        return radius * radius * 3.14
    }
}
//継承関係があるクラスに設定する場合は親クラスを左側に書きます
class ViewController: UIViewController ,Figure{
    override func viewDidLoad() {
        let square = Square(bottom: 10, height: 10)
        let triangle = Triangle(bottom: 10, height: 10)
        let circle = Circle(radius: 10)
        let figures:[Figure] = [square,triangle,circle]
        var totalArea: Float = 0
        figures.enumerated().forEach {
            totalArea += $0.element.culcArea()
        }
        print(totalArea)
    }
    //FigureProtocolを準拠する為のculcArea関数
    func culcArea() -> Float{
        return 0
    }
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.

    }
}

ClassOnlyProtocolとは

前の章で構造体や列挙型にもプロトコルの使用が可能と書きましたが、構造体と列挙型等に制限を行いクラスのみで設定できるプロトコルを作成する事も可能です。

//デリゲートの宣言
protocol FigureDelegate : AnyObject {
    func delegateMethod(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を設定しているのに値型が入る可能性があります。

使用例

//デリゲートの宣言
protocol FigureDelegate : AnyObject {
    func delegateMethod(area: Float)
}

class Triangle {
    let bottom,height: Float

    var delegate: FigureDelegate?
    init(bottom: Float , height: Float) {
        self.bottom = bottom
        self.height = height
    }
    func callDelegate(){
        //delegateMethodの呼び出し
        delegate?.delegateMethod(area: bottom * height / 2 )
    }
}

class ViewController: 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.
    }
}

最後に

プロトコルの設定方法をまとめさせていただきました。
プロトコルの拡張は複雑になっているため、また今度改めてまとめられたらと思っております。

おすすめ書籍

    

-- NORMAL --
suzuki

シェア
執筆者:
suzuki
タグ: Swift

最近の投稿

フロントエンドで動画デコレーション&レンダリング

はじめに 今回は、以下のように…

3週間 前

Goのクエリビルダー goqu を使ってみる

はじめに 最近携わっているとあ…

4週間 前

【Xcode15】プライバシーマニフェスト対応に備えて

はじめに こんにちは、suzu…

2か月 前

FSMを使った状態管理をGoで実装する

はじめに 一般的なアプリケーシ…

3か月 前