kazuki’s blog

新人エンジニアのブログ

SwiftのProtocol(プロトコル)とextension(エクステンション)

こんにちは!かずきです。
今回はProtocolとExtensionについて書いていこうと思います。

※今回の記事内のプロパティはコンピューテッド・プロパティ を指します。

Protocol(プロトコル)

Protocolとは

多言語ではよくインタフェースと呼ばれるものとほぼ同じものです。
継承したクラスで実装すべき、プロパティやメソッドを宣言する為に利用します。

継承先でのプロパティ・メソッドの存在保証

プロトコルで宣言したもは継承クラスで実装されていないとエラーが出るので、
継承クラスでのプロパティ・メソッドが利用可能な事が保証されます。
また、他のクラスと同じように継承先クラスは継承元であるプロトコルの型の変数で保持する事が出来ます。

実装例

protocol HogeHogeProtocol
{
    var Fuga:String {get set}
    
    func  FugaFuga() -> Void
}

このようなプロトコルを用意して、

class HogeHogeHoge : HogeHogeProtocol
{
    var fuga:String!
    
    // 実装しないとエラーになる
    var Fuga: String
    {
        get
        {
            return self.fuga
        }
        set
        {
            self.fuga = newValue
        }
    }
    
    // 実装しないとエラーになる
    func  FugaFuga() -> Void{
        print(self.Fuga)
    }
}

このように継承・プロトコル実装します。

var hogehoge:HogeHogeProtocol = HogeHogeHoge()

hogehoge.Fuga = "ほげほげ"
hogehoge.FugaFuga() // ほげほげ

そうするとプロトコルで宣言されたメソッドを継承クラスで利用出来ます。

extension(エクステンション)

extensionとは

extension(拡張)の意味の通り、クラス・データ型・構造体などの機能を拡張する機能です。 プロパティとメソッドを実装する事ができます。

今回はデータ型とクラスの拡張を紹介します。

データ型の拡張

データを処理するプロパティ

このようなプロパティを書くと

extension Int
{
    // パーセントから割合に変換
    var Rate: Float
    {
        return (Float)(self) / 100.0
    }
}

print(80.Rate) // 0.8

このようにデータ型に格納している値を処理追加する事が出来ます。

データ型そのものに機能を追加するプロパティ

プロパティの前にstaticをつける事で、

extension Int
{
    static var Zero : Int
    {
        return 0
    }
}

print(Int.Zero) // 0

データ型そのものを拡張する事ができます。

データの処理をするメソッド

引数が必要な処理や保持しているデータ内容を変更したい場合などはプロパティでは実装出来ないので、メソッドを使って拡張します。

extension Int
{
    func Square(x : Int) -> Int64 {
        var result:Int64 = Int64(self)
        for _ in 0...x {
            result = result * Int64(self)
        }
        return result
    }
}

print(12.Square(x: 10)) // 8916100448256

クラスの拡張

クラスの拡張もデータ型と同じように行えますが、
selfで取得できる物がデータでは無く、クラスのインスタンスになります。
※基本的にプロトコル実装以外での自作クラスの拡張は処理が分散して分かりにくくなるので、
クラス内に実装する事をおすすめします。(出番はライブラリなどの拡張のみ)

またエクステンションはクラス外の扱いになるので、privateの変数やメソッドにはアクセス出来ません。

// HogeHoge.swift

class HogeHoge
{
    private var hogeStr = "HogeHoge"
}
// HogeHogeExtension.swift

extension HogeHoge
{
    public var HogeStr:String
    {
        get
        {
            return self.hogeStr // エラー
        }
    }
}

プロトコル実装などをクラス外に書いて整理したい場合にも使えます。
この際、クラスとプロトコル実装のエクステンションを同一ファイル内に書く場合は fileprivateという同一ファイル内でのみアクセス可能なアクセス指定子を活用する事が出来ます。

extensionでプロトコル実装をする場合は以下のように記述します。

extension 元のクラス : 実装するプロトコル

 
fileprivateを活用して実装するとこんな感じになります。

// HogeStrProperty.swift

protocol HogeStrProperty {
    var HogeStr:String{ get }
}
// HogeHoge.swift

class HogeHoge {
    fileprivate let hogeStr = "HogeHoge"
}

extension HogeHoge:HogeStrProperty {
    public var HogeStr:String {
        get {
            return self.hogeStr
        }
    }
}

これを他ファイルから呼び出すと

// FugaFuga.swift

class FugaFuga {

//---省略---

func PrintHogeHoge() -> Void {
    let hogeHoge = HogeHoge()
    print(hogeHoge.HogeStr)  // HogeHoge
}

// ---省略---
}

HogeHogeクラスのhogeStrの内容が表示されます。

まとめ

  • プロトコルは動作(鳴く・動くetc)は同じだけど、
    クラス毎にデータ(鳴き声・速度etc)が違ったり、
    処理(動き方etc)など違う物を宣言しておき、
    継承クラスで確実に利用したい場合に利用する

  • エクステンションはデータ型にプロジェクト内でよく使う処理などの追加や、
    ライブラリの拡張・プロトコル実装などに利用する

上述のように使うと便利かと思います。
他にこんな使い方あるよいう方。ぜひコメントなどで教えて下さい!!