Protocolとは、プロトコルを採用する型が満たすべき要件(プロパティとメソッド)のセットです。Swiftでは、プロトコルはProtocol-Oriented Programming(POP)の中心的な要素であり、継承なしでポリモーフィズムを実現します。Swift.org(2025)によると、Swift標準ライブラリの型の85%でプロトコルが使用されています。Objective-Cでは、@protocolも同様の役割を果たしますが、制限があります — クラスのみに適用されます。
重要ポイント
Protocol Swiftにおけるプロトコルは、型が満たすべき要件を定義する抽象インターフェースです。プロトコルは、プロパティ(getter/setter付き)、メソッド(インスタンスおよび型)、イニシャライザ、サブスクリプトを要求できます。クラスとは異なり、プロトコルは構造体、列挙型、クラスで採用可能です — 純粋なクラスベース言語では得られない柔軟性を提供します。
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(POP)は、AppleがWWDC 2015でクラスベース継承の代替として導入したパラダイムです。POPでは、プロトコルが主要な抽象化ツールであり、プロトコル拡張がデフォルト実装を提供します。これにより「菱形継承」問題が解決され、ソースコードを変更せずに既存の型を拡張できます。
| 側面 | POP(プロトコル指向) | OOP(クラスベース) |
|---|---|---|
| 抽象化の単位 | Protocol | 基底クラス |
| 再利用メカニズム | Protocol extension | 継承 |
| 値型 | 対応(struct) | 参照型のみ |
| 複数採用 | Protocol composition | 多重継承(Swiftでは不可) |
| 結合リスク | 低い(疎結合) | 高い(固定階層) |
Apple Swiftブログ(2025)によると、最新のSwiftアプリケーションでは型の70%が構造体です。POPはこの移行の主要な理由です:プロトコルにより、構造体はクラスに切り替えることなくポリモーフィックな振る舞いを得られます。
Swiftプロトコルは、JavaインターフェースとC++抽象クラスの中間に位置します。実装を含めることはできますが(拡張経由)、状態(格納プロパティ)を持つことはできません。3つの言語の違いを見てみましょう。
| 特徴 | Swift Protocol | Java Interface | C++ Abstract Class |
|---|---|---|---|
| メソッド実装 | 可(extension) | 可(default methods) | 可 |
| 格納プロパティ | 不可 | 不可 | 可 |
| 値型 | 可 | 不可 | 不可 |
| 複数実装 | 可 | 可 | 可 |
| イニシャライザ | 可(要件) | 不可 | 可 |
主な違い:Swift Protocolはイニシャライザとサブスクリプトを要求できますが、Java Interfaceでは不可能です。ただし、プロトコルは状態を格納できません — これはプロトコルを採用する型の責任です。
Associated Type(関連型)はプロトコル内部のジェネリック型で、採用する型によって具体化されます。これにより、具体的なデータ型を指定せずに型安全なプロトコルを作成できます。関連型は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 — &演算子による複数プロトコルの組み合わせ。コンポジションに準拠する型は、結合されたすべてのプロトコルを実装する必要があります。これは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なしでオプショナルメソッドを実装する仕組みです。
@protocol Objective-Cの@protocolはSwift Protocolの前身ですが、大きな制限があります。Objective-Cプロトコルはクラスでのみ利用可能で(構造体や列挙型では不可)、メッセージパッシングによる動的ディスパッチを使用します。メソッドは@required(デフォルト)または@optionalにできます。
@protocol Loggable
@required
- (void)logMessage: (NSString *)message;
@optional
- (NSString *)logPrefix;
@end
@interface ConsoleLogger : NSObject
@end
@implementation ConsoleLogger
- (void)logMessage: (NSString *)message {
NSLog(@"[LOG] %@", message);
}
@endSwiftとは異なり、Objective-Cプロトコルは関連型、ジェネリクス、プロトコル拡張、値型をサポートしません。これらはクラスベースの抽象化メカニズムに留まっていますが、Swift Protocolは全型に対応した本格的なポリモーフィズムツールです。
よくある質問
Protocolは状態(格納プロパティ)を保持できません — プロパティ要件のみです。抽象クラスはデータフィールドを含めます。プロトコルは構造体や列挙型でも採用できますが、抽象クラスはクラスでのみサブクラス化できます。Swiftでは、プロトコルが主要な抽象化ツールであり、クラスの使用頻度は低くなっています。
POPは、プロトコルとプロトコル拡張が深い継承階層を置き換えるパラダイムです。すべてのサブクラスが継承する基底クラスの代わりに、POPは拡張によるデフォルト実装を持つプロトコルコンポジションを使用します。これにより結合度が低下し、コードの再利用性が向上します。
はい、Swiftプロトコルは継承をサポートします。protocol SerializableDrawable: Drawable, Codable — DrawableとCodableの要件を組み合わせたプロトコルです。SerializableDrawableを採用する型は、階層内のすべてのプロトコルの要件を満たす必要があります。これはクラス継承とは異なります — プロトコルには共通の祖先がありません。
some(曖昧型)は関数がプロトコルに準拠する特定の型を返すことを保証します。any(存在型)はプロトコルに準拠する任意の型を格納できます。someは型の同一性を保持するために使用され、anyは異種コレクションに使用されます。someはSwift 5.1で、anyはSwift 5.7で導入されました。
確認にはisを、キャストにはas?を使用します:if let drawable = object as? Drawable { drawable.draw(in: ctx) }。Objective-CではconformsToProtocol:を使用します。Swiftは関連型のないプロトコルに対してisチェックもサポートしています。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。