Protocol — その概要、iOSのプロトコルとデリゲート

著者: IT Sectr 公開日: 2026-02-18 読了時間: 10 分

Protocolとは、プロトコルを採用する型が満たすべき要件(プロパティとメソッド)のセットです。Swiftでは、プロトコルはProtocol-Oriented Programming(POP)の中心的な要素であり、継承なしでポリモーフィズムを実現します。Swift.org(2025)によると、Swift標準ライブラリの型の85%でプロトコルが使用されています。Objective-Cでは、@protocolも同様の役割を果たしますが、制限があります — クラスのみに適用されます。

重要ポイント

  • Protocol — 構造体、クラス、列挙型が実装すべきインターフェースを定義する契約
  • Protocol-Oriented Programming — プロトコルとプロトコル拡張が深い継承階層を置き換えるSwiftのパラダイム
  • Protocol extension — @objcなしでプロトコルメソッドをオプションにするデフォルトメソッド実装
  • Associated types — 異なるデータ型で型安全に動作するための関連型を持つジェネリックプロトコル
  • Protocol composition — &演算子による複数プロトコルの組み合わせで要件を正確に指定

Swiftのプロトコルとは?

Protocol Swiftにおけるプロトコルは、型が満たすべき要件を定義する抽象インターフェースです。プロトコルは、プロパティ(getter/setter付き)、メソッド(インスタンスおよび型)、イニシャライザ、サブスクリプトを要求できます。クラスとは異なり、プロトコルは構造体、列挙型、クラスで採用可能です — 純粋なクラスベース言語では得られない柔軟性を提供します。

Swift
protocol Drawable {
    var boundingRect: CGRect { get }
    mutating func draw(in context: CGContext)
}

struct Circle: Drawable {
    var center: CGPoint
    var radius: CGFloat
    
    var boundingRect: CGRect {
        CGRect(x: center.x - radius, y: center.y - radius,
               width: radius * 2, height: radius * 2)
    }
    
    func draw(in context: CGContext) {
        context.addEllipse(in: boundingRect)
    }
}

Circle構造体はDrawableプロトコルを採用し、boundingRectプロパティとdraw(in:)メソッドを提供します。プロトコルのおかげで、Drawableを実装する任意の図形を統一的に扱えます — これは共通の基底クラスから継承せずにポリモーフィズムを実現する方法です。

Protocol-Oriented Programming:Swiftのパラダイム

Protocol-Oriented Programming(POP)は、AppleがWWDC 2015でクラスベース継承の代替として導入したパラダイムです。POPでは、プロトコルが主要な抽象化ツールであり、プロトコル拡張がデフォルト実装を提供します。これにより「菱形継承」問題が解決され、ソースコードを変更せずに既存の型を拡張できます。

側面POP(プロトコル指向)OOP(クラスベース)
抽象化の単位Protocol基底クラス
再利用メカニズムProtocol extension継承
値型対応(struct)参照型のみ
複数採用Protocol composition多重継承(Swiftでは不可)
結合リスク低い(疎結合)高い(固定階層)

Apple Swiftブログ(2025)によると、最新のSwiftアプリケーションでは型の70%が構造体です。POPはこの移行の主要な理由です:プロトコルにより、構造体はクラスに切り替えることなくポリモーフィックな振る舞いを得られます。

プロトコル vs 抽象クラス vs インターフェース

Swiftプロトコルは、JavaインターフェースとC++抽象クラスの中間に位置します。実装を含めることはできますが(拡張経由)、状態(格納プロパティ)を持つことはできません。3つの言語の違いを見てみましょう。

特徴Swift ProtocolJava InterfaceC++ Abstract Class
メソッド実装可(extension)可(default methods)
格納プロパティ不可不可
値型不可不可
複数実装
イニシャライザ可(要件)不可

主な違い:Swift Protocolはイニシャライザとサブスクリプトを要求できますが、Java Interfaceでは不可能です。ただし、プロトコルは状態を格納できません — これはプロトコルを採用する型の責任です。

プロトコルにおけるAssociated TypesとGenerics

Associated Type(関連型)はプロトコル内部のジェネリック型で、採用する型によって具体化されます。これにより、具体的なデータ型を指定せずに型安全なプロトコルを作成できます。関連型はSwiftのジェネリクスと連携して動作します。

Swift
protocol Container {
    associatedtype Item
    var count: Int { get }
    mutating func append(_ item: Item)
    subscript(i: Int) -> Item { get }
}

struct IntStack: Container {
    var items: [Int] = []
    var count: Int { items.count }
    typealias Item = Int
    
    mutating func append(_ item: Int) {
        items.append(item)
    }
    
    subscript(i: Int) -> Int {
        items[i]
    }
}

func sum<C: Container>(_ container: C) -> C.Item where C.Item: Numeric {
    // 関数の本体
}

Containerはassociatedtype Itemを持つプロトコルです。IntStackはtypealias Item = Intを指定して実装します。ジェネリック関数sumはwhere句を使用して数値コンテナのみで動作します。関連型により、型安全性を失うことなくプロトコルをジェネリックにできます。

Protocol compositionとプロトコル拡張

Protocol composition — &演算子による複数プロトコルの組み合わせ。コンポジションに準拠する型は、結合されたすべてのプロトコルを実装する必要があります。これはSwiftにない多重継承を代替し、関数パラメータの要件を正確に指定できます。

Swift
protocol Named { var name: String { get } }
protocol Aged { var age: Int { get } }

struct Person: Named, Aged {
    let name: String
    let age: Int
}

func greet(_ entity: Named & Aged) {
    print("こんにちは、\(entity.name)、年齢 \(entity.age)!")
}

// Protocol extension — デフォルト実装
extension Named {
    func introduce() {
        print("私の名前は\(name)です")
    }
}

Protocol extensionはデフォルトのメソッド実装を提供します。プロトコルを採用する型は拡張メソッドをオーバーライドでき、その場合は自身の実装が呼び出されます。これがSwiftが@objcなしでオプショナルメソッドを実装する仕組みです。

Objective-Cの@protocol:Swiftとの違い

@protocol Objective-Cの@protocolはSwift Protocolの前身ですが、大きな制限があります。Objective-Cプロトコルはクラスでのみ利用可能で(構造体や列挙型では不可)、メッセージパッシングによる動的ディスパッチを使用します。メソッドは@required(デフォルト)または@optionalにできます。

Objective-C
@protocol Loggable
@required
- (void)logMessage: (NSString *)message;
@optional
- (NSString *)logPrefix;
@end

@interface ConsoleLogger : NSObject 
@end

@implementation ConsoleLogger
- (void)logMessage: (NSString *)message {
    NSLog(@"[LOG] %@", message);
}
@end

Swiftとは異なり、Objective-Cプロトコルは関連型、ジェネリクス、プロトコル拡張、値型をサポートしません。これらはクラスベースの抽象化メカニズムに留まっていますが、Swift Protocolは全型に対応した本格的なポリモーフィズムツールです。

よくある質問

プロトコルと抽象クラスの違いは?

Protocolは状態(格納プロパティ)を保持できません — プロパティ要件のみです。抽象クラスはデータフィールドを含めます。プロトコルは構造体や列挙型でも採用できますが、抽象クラスはクラスでのみサブクラス化できます。Swiftでは、プロトコルが主要な抽象化ツールであり、クラスの使用頻度は低くなっています。

Protocol-Oriented Programmingとは?

POPは、プロトコルとプロトコル拡張が深い継承階層を置き換えるパラダイムです。すべてのサブクラスが継承する基底クラスの代わりに、POPは拡張によるデフォルト実装を持つプロトコルコンポジションを使用します。これにより結合度が低下し、コードの再利用性が向上します。

プロトコルは別のプロトコルを継承できますか?

はい、Swiftプロトコルは継承をサポートします。protocol SerializableDrawable: Drawable, Codable — DrawableとCodableの要件を組み合わせたプロトコルです。SerializableDrawableを採用する型は、階層内のすべてのプロトコルの要件を満たす必要があります。これはクラス継承とは異なります — プロトコルには共通の祖先がありません。

プロトコルの文脈でanyとsomeとは?

some(曖昧型)は関数がプロトコルに準拠する特定の型を返すことを保証します。any(存在型)はプロトコルに準拠する任意の型を格納できます。someは型の同一性を保持するために使用され、anyは異種コレクションに使用されます。someはSwift 5.1で、anyはSwift 5.7で導入されました。

Swiftでプロトコル準拠を確認するには?

確認にはisを、キャストにはas?を使用します:if let drawable = object as? Drawable { drawable.draw(in: ctx) }。Objective-CではconformsToProtocol:を使用します。Swiftは関連型のないプロトコルに対してisチェックもサポートしています。

まとめ

  • Protocol — 採用する型のプロパティ、メソッド、イニシャライザ、サブスクリプトの要件を定義する契約
  • Protocol-Oriented Programmingはクラス継承をプロトコルコンポジションと拡張で置き換える
  • Protocol extensionはデフォルト実装を提供し、@objcなしでメソッドをオプションにする
  • Associated typesにより、実装側で型を具体化するジェネリックプロトコルを作成可能
  • Protocol compositionは&で複数プロトコルの要件を結合する
  • Objective-C @protocolはクラスに限定され、関連型、ジェネリクス、値型をサポートしない
  • 推奨:Swiftではプロトコルを主要な抽象化ツールとして使用し、参照セマンティクスが必要な場合のみクラスを使用する

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください