Delegateは、あるオブジェクトが事前に定義されたメソッドを持つプロトコルを通じて、別のオブジェクトにタスクの実行を委譲するデザインパターンです。iOS開発において、DelegateはCocoa Touchの基本的なパターンの1つであり、送信者と受信者の直接的な結合なしに非同期通知を行うために使用されます。Apple Documentation(2025)によると、委譲はFoundationとUIKitでテーブルイベント、ネットワークリクエスト、位置情報管理の処理に使用されています。このパターンはコンポーネントの疎結合とコードの再利用を保証します。
重要なポイント
Delegate(デリゲート)は、特定のプロトコルを実装し、別のオブジェクトのイベントに関する通知を受け取るオブジェクトです。委譲パターンは継承の代替手段です:メソッドをオーバーライドするためにサブクラスを作成する代わりに、オブジェクトはイベント処理を外部オブジェクトに委譲します。iOSでは、委譲は必須メソッドとオプションメソッドを持つSwiftプロトコルを通じて実装されます。delegateプロパティは常にweak varとして宣言され、オブジェクト間の循環参照を防ぎます。
デリゲートプロトコルは、オブジェクト間の相互作用の契約を定義します。必須メソッドはデリゲートによって実装される必要があり、そうでなければコードはコンパイルされません。オプションメソッドは@objc optional属性でマークされ、デリゲートが関連するイベントにのみ応答できるようにします。メソッド名は規約に従います:最初のパラメータは送信者オブジェクト、2番目はイベントデータです。例えば、tableView(_:didSelectRowAt:)は、送信者がUITableViewであり、データが選択された行のインデックスであることを示します。
// Delegateプロトコル
protocol DownloadManagerDelegate: AnyObject {
func downloadManager(_ manager: DownloadManager,
didFinishWith data: Data)
func downloadManager(_ manager: DownloadManager,
didFailWith error: Error)
@objc optional func downloadManager(_ manager: DownloadManager,
didUpdateProgress progress: Float)
}
// Delegateを使用するクラス
class DownloadManager {
weak var delegate: DownloadManagerDelegate?
func startDownload(from url: URL) {
URLSession.shared.dataTask(with: url) { [weak self] data, _, error in
guard let self else { return }
if let error = error {
self.delegate?.downloadManager(self, didFailWith: error)
} else if let data = data {
self.delegate?.downloadManager(self, didFinishWith: data)
}
}.resume()
}
}
delegateプロパティは、 retain cycleを防ぐためにweak varとして宣言する必要があります。参照が強参照であった場合、デリゲートと委譲元オブジェクトは互いに保持し合い、ARCはそれらのメモリを解放できなくなります。デリゲートプロトコルはAnyObject(クラスのみ)を継承し、weakの使用を可能にします。構造体と列挙型は値 semantics のためデリゲートになることはできません。値型の代替手段はコールバッククロージャです。
class ViewController: DownloadManagerDelegate {
let manager = DownloadManager()
override func viewDidLoad() {
super.viewDidLoad()
manager.delegate = self // weak — retain cycleなし
manager.startDownload(from: url)
}
func downloadManager(_ manager: DownloadManager, didFinishWith data: Data) {
processData(data)
}
func downloadManager(_ manager: DownloadManager, didFailWith error: Error) {
showError(error)
}
}
Delegateパターンは1対1で機能します:1つの送信者オブジェクトは特定の時点で1つのデリゲートしか持つことができません。イベントが発生すると、送信者はデリゲートが設定されているかどうかを確認し、対応するプロトコルメソッドを呼び出します。直接呼び出しに対する利点は、送信者がデリゲートの型を知らず、プロトコルに準拠していることだけを知っていることです。これはSOLIDの依存関係逆転の原則(DIP)に準拠しています。
デリゲートは代入によって割り当てられます:someObject.delegate = self。デリゲートが解放されると、weak semanticsによりプロパティは自動的にnilになります。デリゲートメソッドを呼び出す前に、オプショナルチェイニングを通じてデリゲートがチェックされます:delegate?.method()。delegateがnilの場合、呼び出しはクラッシュせずに無視されます。オプションのプロトコルメソッドについては、追加のチェックが使用されます:delegate?.responds(to: #selector(...))。ただしSwiftでは、このチェックは通常、オプショナルメソッド宣言を通じて暗黙的に行われます。
マルチスレッド環境では、デリゲートは非同期結果の返却に使用されます。URLSessionは、データ受信時、タイムアウト時、認証エラー時に呼び出されるメソッドを持つURLSessionDelegateを提供します。デリゲートメソッドはURLSessionのバックグラウンドキューで実行されるため、UI更新のためにはメインキューへのディスパッチが必要です。非同期デリゲートは呼び出しスレッドをブロックせず、他のタスクを続行できます。
class NetworkService: NSObject, URLSessionDataDelegate {
private lazy var session = URLSession(
configuration: .default,
delegate: self,
delegateQueue: OperationQueue()
)
private var receivedData = Data()
func urlSession(_ session: URLSession,
dataTask: URLSessionDataTask,
didReceive data: Data) {
receivedData.append(data)
let progress = Float(receivedData.count) / Float(expectedSize)
DispatchQueue.main.async {
self.progressHandler?(progress)
}
}
func urlSession(_ session: URLSession,
task: URLSessionTask,
didCompleteWithError error: Error?) {
if let error = error {
delegate?.networkService(self, didFailWith: error)
} else {
delegate?.networkService(self, didReceive: receivedData)
}
}
}
DelegateとCallbackは同じ問題(非同期通知)を解決しますが、方法が異なります。Delegateは名前付きメソッドを持つプロトコルを使用し、Callbackはコンテキストキャプチャを持つクロージャを使用します。選択は、イベントの数、シグネチャの複雑さ、アーキテクチャの好みに依存します。Appleは、複数のイベントを持つAPIにはDelegate(UITableView — 20以上のメソッド)を、1回限りの完了にはCallbackを推奨しています。
Delegateは、単一ソースからの複数の異なるイベントを処理する場合に適しています。例えば、CLLocationManagerは、位置情報の変更、許可エラー、ジオフェンスの出入り、サービスのステータス変更をデリゲートに通知します。各イベントは、明確な名前と型付けされたパラメータを持つ個別のプロトコルメソッドです。Delegateは動作設定(should、will、didメソッド)にも便利です。
Callbackは、単一の結果を持つ1回限りのリクエストに対してよりシンプルです。URLSession.dataTaskのCompletion handlerは、呼び出し箇所で少なくとも3つのプロトコルメソッドに対して1行で済みます。Callbackは関数型チェーン(map、flatMap、async/await)にもより自然です。ただし、2〜3レベルを超えるネストでは、CallbackはCallback Hellになりますが、Delegateは常にフラットなままです。
iOS SDKには、さまざまなサブシステム用の数十の組み込みデリゲートプロトコルが含まれています。それぞれが特定の相互作用シナリオ用に設計されています。Apple Documentation(2025)によると、最も使用されているデリゲートは、UITableViewDelegate、UITextFieldDelegate、CLLocationManagerDelegate、URLSessionDelegate、UNUserNotificationCenterDelegateです。これらのプロトコルには、必須レベルの異なる3〜30のメソッドが含まれています。
UITableViewDelegateは、テーブルセルの外観と動作を管理します。行の選択処理、セルの高さ設定、カスタムヘッダー/フッタービュー、スワイプアクションのためのメソッドが含まれています。すべてのプロトコルメソッドはオプションであり、必要な機能のみを実装できます。デリゲートがない場合、テーブルはデフォルト設定で動作します。歴史的に、デリゲートはUITableViewDataSourceと組み合わされていました。
URLSessionDelegateは、HTTPリクエストの詳細な制御を提供します。デリゲートメソッドは、サーバー応答の受信時、データ到着時、ダウンロード完了時に呼び出されます。特殊化されたサブプロトコルURLSessionTaskDelegateとURLSessionDataDelegateは、特定のタスクタイプの基本機能を拡張します。デリゲートは、バックグラウンドダウンロード、SSL証明書、カスタムリダイレクト処理をサポートするために必要です。
| Delegate | メソッド数 | 目的 |
|---|---|---|
| UITableViewDelegate | 25 | テーブルの外観と操作 |
| UITextFieldDelegate | 8 | テキスト入力とキーボード処理 |
| CLLocationManagerDelegate | 12 | 位置情報の更新とジオフェンス |
| URLSessionDelegate | 6 | HTTPセッションと証明書管理 |
| UNUserNotificationCenterDelegate | 4 | フォアグラウンドでのプッシュ通知処理 |
メモリ管理は、iOSでデリゲートを使用する際の重要な側面です。ARC(自動参照カウント)はメモリを自動的に管理しますが、weak/unowned参照の適切な使用が必要です。ルールに違反すると、メモリリークや早期解放が発生します。strongとして宣言されたデリゲートは、デリゲートの所有者が委譲元オブジェクトへの参照も保持している場合、 retain cycleを引き起こします。
Retain cycleは、オブジェクトA(所有者)が自身をオブジェクトBのデリゲートとして設定し、Bがデリゲートへの強参照を保持する場合に発生します。例:ViewControllerがURLSessionを作成し、自身をセッションのデリゲートとして設定しますが、delegateQueueが指定されていない場合、URLSessionはデフォルトでデリゲートへの強参照を保持します。解決策は、デリゲート参照タイプ(weakまたはstrong)についてAPIドキュメントを常に確認し、deinitでデリゲートを明示的にnilにすることです。
class SafeViewController: UIViewController {
private var session: URLSession?
private var service: NetworkService?
override func viewDidLoad() {
super.viewDidLoad()
service = NetworkService()
service?.delegate = self
}
deinit {
// deinitでdelegateをnilに設定 — best practice
service?.delegate = nil
session?.invalidateAndCancel()
}
}
// NSObjectを介したweak delegate付きURLSession
class WeakDelegateSession: NSObject {
private weak var delegate: URLSessionDelegate?
func createSession() -> URLSession {
let queue = OperationQueue()
queue.maxConcurrentOperationCount = 1
return URLSession(
configuration: .default,
delegate: self,
delegateQueue: queue
)
}
}
デリゲートメソッドを呼び出す前に、デリゲートが存在し(nilではなく)、呼び出されたメソッドを実装していることを確認する必要があります。必須プロトコルメソッドの場合、チェックは不要です。コンパイラが実装を保証します。オプションメソッドの場合は、respond(to:)またはオプショナルチェイニングを使用します。デリゲートが解放された場合、weak参照は自動的にnilになり、デリゲート呼び出しは無視されます。これは追加の処理を必要としない安全な動作です。
開発者は、特にiOS学習の初期段階において、Delegateパターンを使用する際にしばしばエラーを犯します。最も一般的なものは次のとおりです:strongデリゲートによる retain cycle、delegate?.method()の呼び出し忘れ、誤ったプロトコルメソッドシグネチャ、操作開始後のデリゲート設定、マルチスレッドの衝突。各エラーとその防止方法を見てみましょう。
最も重大なエラーは、delegateプロパティをweak varの代わりにstrong varとして宣言することです。これにより、デリゲートと委譲元オブジェクトの両方が解放できない retain cycleが発生します。結果:メモリリーク、アプリの遅延、隠れたバグ。解決策:デリゲートには常にweak varを使用し、プロトコルをAnyObjectから継承させて、値型がデリゲートとして使用されるのを防ぎます。
非同期メソッドを呼び出した後にデリゲートを設定すると、最初のイベントが失われる可能性があります。例:manager.delegate = selfを割り当てる前にstartDownload()を呼び出すと、ダウンロードが同期的または非常に高速に実行された場合に完了コールバックが失われます。解決策:非同期メソッドを呼び出す前にデリゲートを設定し、プロトコルコメントで初期化順序を文書化します。
よくある質問
Weakは、デリゲートと委譲元オブジェクト間の retain cycleを防ぎます。参照が強参照であった場合、オブジェクトは互いに保持し合い、ARCはそれらを解放できなくなります。weak参照は、デリゲートが解放されると自動的にnilになります。これはObjective-Cの登場以来の標準的なCocoa Touchのプラクティスであり、後方互換性のためにSwiftでも維持されています。
Delegateはイベントを処理し、動作を管理します(セルの高さ、タップへの応答)。DataSourceは表示用のデータを提供します(行数、セル)。デリゲートは “どのように?” という質問に答え、dataSourceは “何を?” という質問に答えます。iOSでは、両方ともプロトコルを通じて実装され、多くの場合同じコントローラ内にありますが、概念的に分離されています。
いいえ、プロトコルがAnyObject(クラスプロトコル)を継承している場合、使用できません。weak参照は参照型(クラス)でのみ使用できます。値型(struct、enum)の場合は、コールバッククロージャまたは個別のラッパークラスを使用してください。プロトコルを制御できる場合は、AnyObjectの継承を避けることができますが、その場合weakは禁止されます。weakデリゲートとstructデリゲートの間で意識的に選択してください。
responds(to:)は、オブジェクトが指定されたセレクタを実装しているかどうかをチェックするNSObjectProtocolのメソッドです。呼び出し前にオプションの@objcプロトコルメソッドをチェックするために使用されます。このチェックがないと、実装されていないオプションメソッドを呼び出すとNSInvalidArgumentExceptionが発生します。Swiftでは、@objc optionalを持つプロトコルの場合、チェックはオプショナルバインディングを通じて暗黙的に行うことができます。
いいえ、delegateは委譲パターンであり、シングルトンではありません。シングルトンとは異なり、デリゲートは実行時に置き換えることができ、各委譲元オブジェクトに対して単一のインスタンスとして存在します。1つのオブジェクトが複数の送信者のデリゲートになることができます。シングルトンは単一のクラスインスタンスを保証する生成パターンであり、委譲とは関係がありません。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。