【Swift】プロトコルの使い方とメリットとは?実装を任意にする方法

【Swift】プロトコルの使い方とメリットとは?実装を任意にする方法

この記事からわかること

  • Swiftプロトコルとは?
  • メリット使い方
  • SwiftUIViewプロトコル
  • someの意味
  • メソッドの実装義務にしない方法
  • optional@objcとは?

index

[open]

\ アプリをリリースしました /

みんなの誕生日

友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-

posted withアプリーチ

Swiftのプロトコルに関することについてまとめていきます。

プロトコルとは?

Swiftにはプロトコルと呼ばれる規格を定義できる機能が存在します。プロトコルという言葉自体はSwiftに限ったものではなく、IT業界では「規約」と言った意味のニュアンスで使われています。

日常の中でもURLの接頭辞になっている「http」やファイル送信をするための「FTP」などデータをやり取りする際のフォーマット規約として目にすることも多いかと思います。

一方Swiftでのプロトコルは構造体やクラスなどを定義する際の構造規格の意味を持っています。Swiftにおけるプロトコルという概念は前身である「Objective-C」から実装されている仕様であり、同じような使い方でSwiftにも用いられています。

Objective-Cでのプロトコル

@protocol Character
- (void) openStatus;
@end
 
@interface brave:NSObject  {}   
@end

@implementation brave
- (void) openStatus{printf("ステータス");}       
@end

使い方と特徴とメリット

プロトコルの使う手順は以下の通りです。

  1. プロトコルを定義する
  2. 構造体(クラス)の宣言時にプロトコルを指定する

プロトコルは構造体やクラスに指定して使用します。定義しただけでは意味がありません。

Swiftのプロトコルの大きな特徴は宣言したプロトコルに準拠した構造体やクラスを作成する際にそのプロトコルが定義しているプロパティとメソッドの実装が必須になることです。

実装し忘れるとエラーを吐いてしまうので、そもそもコンパイルできず動作が停止してしまいます。

プロトコルに準拠していない時のエラー

Type 'TargetPerson' does not conform to protocol 'human'

この仕様がプロトコルの大きなメリットでもあります。プロトコルを定義するだけで実装して欲しい処理などを強制することができ、自分以外の人が実装する場合でも実装漏れをなくすことができます。

特徴とメリット

Swiftにおけるプロトコルの定義

Swiftでプロトコルを定義するにはprotocolをつけて以下のように宣言します。

protocol human {
    var name: String { get set }
    var age: Int { get set }
    func selfIntroduction()
}

プロパティを定義する際は型の指定とともに{ get set }も記述しておきます。プロトコル内には特定の値や処理は記述しなくてもOKです。

{ get set }を記述していない場合のエラー

Property in protocol must have explicit { get } or { get set } specifier

準拠した構造体を作成する

定義したプロトコルに準拠させた構造体を作成するには構造体名の後に:プロトコル名形式で記述します。指定された構造体はプロトコルの持つプロパティなどの定義が必須になります。クラスなどに準拠させる場合も形式は変わりません

// humanプロトコルに準じた構造体を生成
struct TargetPerson: human {
    var name = "ame"
    var age = 15
    func selfIntroduction(){
        print("Hi! My Name is ame.")
    }
}

複数のプロトコルに準拠させる

// Characterプロトコルが定義されているとする
struct TargetPerson: human , Character {
    var name = "ame"
    var age = 15
    func selfIntroduction(){
        print("Hi! My Name is ame.")
    }
}

クラスの場合はスーパークラスの後にプロトコル

クラスの場合、スーパークラスを指定することもありますがその際は先にスーパクラスを指定してから、後続に続いてプロトコルを指定します。

// NSObjectクラスをスーパークラスとする
class TargetPerson: NSObject, human {
    var name = "ame"
    var age = 15
    func selfIntroduction(){
        print("Hi! My Name is ame.")
    }
}

Swift UIでのViewプロトコル

Swift UIの心臓部分でもあるViewプロトコルを例として見てみます。Viewプロトコルは読み取り専用のコンピューテッドプロパティであるbodyプロパティのみを持つプロトコルなのでbodyプロパティの定義が必須になります。

struct ContentView: View {
  var body: some View {
    
  }
}

someの意味

プロトコルの前についているsomeの意味が分からなかったので調べてみました。まずは定義を確認してみます。

Viewプロトコルの定義(command + クリックで表示)

public protocol View {
    associatedtype Body : View
    @ViewBuilder var body: Self.Body { get }
}

associatedtype型を確定せずに準拠して決めることを定義する際に使用できるキーワードです。

それでもよく分からなかったのでググってみると以下の記事を参考にするとなんとなく分かった気がします。

つまりvar body: some Viewsomeその型に準じた別の形を隠蔽できる記法のようです。上記の記事に恐ろしくわかりやすく解説されていました。

オプショナルメソッド(実装を必須としないようにする)

プロトコルの醍醐味である「実装を義務付ける仕様」ですが状況に応じては「必須にはしたく無いけどプロトコルとして定義したい」場合もあると思います。

これを解決するのはoptionalキーワードと@objc修飾子です。この2つを以下のように付与するとこの場合はselfIntroductionメソッドの定義は義務ではなくなります。

@objc protocol human {
    var name: String { get set }
    var age: Int { get set }
    @objc optional func selfIntroduction()
}

optionalは言葉通り、nilを許容できるようにしてます。@objcの役割は指定したプロトコルやメソッドをObjective-Cとして認識させる意味合いがあるようです。

しかしこれは諸刃の剣のようで@objcを使うとSwiftの構造体への指定ができなくなってしまいました。クラスや変数への指定は問題なく可能でした。

@objcを使ったプロトコルを構造体に指定した場合のエラー

Non-class type 'TargetPerson' cannot conform to class protocol 'human'

この方法はタブーなのかと思いきやSwiftでは実際にoptionalが使用されたプロトコルが定義されていました。ですが@objcは見当たらなかったので少し見当違いかも?

public protocol CLLocationManagerDelegate : NSObjectProtocol {

    @available(iOS 6.0, *)
    optional func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation])

}

ちなみに@availableiOSのバージョンによって処理を分岐するための修飾子です。

extensionを使って実装不要メソッドを定義する

そもそも実装不要のメソッドを定義したい場合の別の解決方法もありました。

構造体やクラスなどでも使えるextension(拡張)キーワードを使用すればプロトコルとして宣言しつつも実装を任意にすることが可能です。

protocol human {
    var name: String { get set }
    var age: Int { get set }
}

//  拡張して定義する
extension human {
    func selfIntroduction(){}
} 
// 拡張されているhumanプロトコルに準拠
struct TargetPerson: human {
    var name = "ame"
    var age = 15
    //  func selfIntroduction() 実装不要
}

プロトコルへのジェネリックプログラミング

Swiftのプロトコルに対してジェネリックプログラミングな実装をするために用意されているassociatedtypeキーワードが用意されています。

public protocol View {
    associatedtype Body : View
    @ViewBuilder  var body: Self.Body { get }
}

まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。

ご覧いただきありがとうございました。

searchbox

スポンサー

ProFile

ame

趣味:読書,プログラミング学習,サイト制作,ブログ

IT嫌いを克服するためにITパスを取得しようと勉強してからサイト制作が趣味に変わりました笑
今はCMSを使わずこのサイトを完全自作でサイト運営中〜

New Article

index