Xcode
iOS
Swift

【Swift】hogehoge.delegate = self は何をしているのか。

はじめに

プログラミング経験:Ruby3ヶ月。Python3ヶ月
Swift始めて9日目の人間が書きます。至らない点があると思うのでぜひご指摘お願いします。
前回:【Swift】俺はデリゲートなんか使わない。

デリゲートがよくわかりません。

特に、

hogehoge.delegate = self ← コイツ

コイツを理解するために徹底的に調べてみました。

デリゲートについて

デリゲートの概念についてはわかりやすいサイトや記事があるのでそれを参考にしてみてください。

Swift言語を学ぶ
プロトコルとデリゲートのとても簡単なサンプルについて

簡単にまとめると、

デリゲートは他のクラスに処理を委譲したり、通知したりする仕組み

です。

①処理を依頼するクラス
②処理を依頼するクラスと処理を依頼されるクラスを取り持つプロトコル
③処理を依頼されるクラス

の3つの動きを理解するとわかりやすいです。

とりあえず実装してみる

仕組みはわかったけど実装できないと意味がありません。
デリゲートを用いて実装してみます。

「TextField(inputText)に文字列を入力後、returnを押したら Label(outputText)に入力された文字列が表示される」という実装内容です。

032d4bb4a0add2be1948bec3e048227f.gif

ViewController.swift
import UIKit

class ViewController: UIViewController, UITextFieldDelegate { // 追加記述①

  @IBOutlet weak var inputText: UITextField!
  @IBOutlet weak var outputText: UILabel!

  override func viewDidLoad() {
    super.viewDidLoad()
    // 追加記述②
    inputText.delegate = self
  }

  override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
  }

  // 追加記述③
  func textFieldShouldReturn(_ textField: UITextField) -> Bool {
    textField.resignFirstResponder()
    let text = textField.text
    outputText.text = text
    return true
  }
}

どうやら3つの記述を追加することでデリゲートを用いた実装ができるってことがわかります。

それぞれ何をしているのかみていきます。

①ViewControllerクラスがUITextFieldDelegateプロトコルに準拠することを宣言する

クラスがプロトコルに準拠しているとは、クラスがプロトコル(クラスの挙動を決めた設計図)を参照しているといったイメージだと思います。

では、なぜクラスをプロトコルに準拠させるんでしょう。

UITextFieldDelegateプロトコルの定義を確認してみましょう。

スクリーンショット 2017-06-20 17.33.24.png

・UITextFieldDelegateを選択して右クリック(二本指でタップ)
・Jump to Definitionをクリック
(この操作でクラスやプロトコルの定義を確認できます。)

スクリーンショット 2017-06-20 17.33.56.png

UITextDelegateプロトコルにtextFieldShouldReturnメソッドが定義されています!
他にも様々なメソッドが定義されていることがわかります。

UITextFieldDelegateに定義されているtextFieldShouldReturnを使うためにこの実装が必要だったんですね。

まとめると、この実装で

依頼するクラス(ViewController)にこのプロトコル(UITextFieldDelegate)を使いますよと教えてあげていたということになるかと思います。

②inputTextのdelegateの通知先を自分自身に設定する

私はこの

inputText.delegate = self

に最も苦戦しました。日本語訳してみると、

UITextFieldクラスのインスタンスであるinputTextのdelegateプロパティにViewControllerのインスタンスを渡している

です。これがどういったことを意味しているのか理解するのが難しかったです。

※ プロパティについての参考サイト:Swift言語を学ぶ

一旦、

inputText.delegate = self

からは離れて

outputText.text = text

を考えてみましょう。(上記実装内容のtextFieldShouldReturnメソッドの4行目です)

あれ?なんか形似てますね。。。
これは何をしてるのか初心者の私でも直感的にわかりました。

UILabelクラスのインスタンスのtextプロパティにUITextFieldクラスのインスタンスのtextプロパティを渡しています。

UILabelとUITextFieldの定義を確認してみましょう。
スクリーンショット 2017-06-21 11.08.51.png

スクリーンショット 2017-06-21 11.08.25.png

2つとも1行目にtextプロパティが定義されていることが確認できます!

outputText.text = text

outputTextはUITextFieldクラスのインスタンスなので、これは、outputTextのtextプロパティにtextを代入することを意味します。

なるほど、だからこう実装することでUITextField(inputText)に入力された文字列をUILabel(outputText)に渡せるんですね。

それでは、

inputText.delegate = self

に戻ってみましょう。

これは、UITextFieldクラスのインスタンスであるinputTextのdelegateプロパティにViewControllerのインスタンスを渡しているんでしたね。

ふむふむ、なんだかさっきより飲み込めてきた気がします。

inputTextは UITextFieldクラスのインスタンスです。
UITextFieldクラス確認してみます。

スクリーンショット 2017-06-20 17.06.10.png

textプロパティと同じようにdelegateというプロパティが定義されているのが確認できます。

通常、変数の定義の時は型宣言をします。

open var text: String?

textプロパティであればString型が宣言されています。

これは、String型の値を代入できる変数textを定義しています。

※ 変数の型についての参考サイト:「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典

weak open var delegate: UITextFieldDelegate?

同じように、delegateプロパティはUITextFieldDelegateを型宣言しています。
UITextFieldDelegateとはプロトコルです。

プロトコルを型宣言すると、変数にはそのプロトコルに準拠している値(オブジェクト)を格納できるようになります。

つまりこれは、UITextFieldDelegateプロトコルに準拠しているオブジェクトを代入できる変数delegateを定義しています。

※ プロトコルの型宣言についての参考サイト:Swift プログラミング
※ オブジェクトについての参考サイト:クラスとオブジェクトとインスタンスの関係

inputText.delegate = self

inputTextはUITextFieldクラスのインスタンスなので、これはinputTextのdelegateプロパティにselfを代入しているという意味になります。

selfはViewControllerクラスのインスタンスですが、ViewControllerクラスは①でUITextFieldDelegateプロトコルに準拠する宣言をしています。

すなわち、そのインスタンスであるselfもまたUITextFieldDelegateに準拠しています。

つまり、何を意味するかというと、
inputTextがreturnを押されたらViewController(私)に教えてね!
といったことかと思います。

returnが押されたらという処理は③で実装するので、
②単体の意味は、

inputTextでUITextFieldDelegateプロトコルで定義されているメソッドが実行されたらViewController(私)に教えてね!

ということになります。

inputTextのdelegateの通知先を自分自身に設定する
という表現をサイトでよく見かけましたが、ここまで細かく噛み砕いてようやく理解できました。

③delegateから通知してほしい内容を指定する

ここまで理解できてしまえば、③の実装は問題ないかと思います。
依頼したい処理のメソッドとその後の処理を実装します。

delegateから通知してほしいのはreturnが押されたらというタイミングなので、その処理をしてくれるメソッドであるtextFieldShouldReturnを指定します。

まとめ

一般的な説明

クラスがプロトコルに準拠することを宣言する
delegateの通知先を自分自身に設定する
delegateから通知してほしい内容を指定する

自分なりの解釈

依頼するクラス:ViewController
依頼されるクラス:UITextField
プロトコル:UITexgFieldDelegate

処理を依頼するクラスにこのプロトコルを使いますよと教えてあげる
依頼されるクラスのインスタンスでプロトコルで定義されているメソッドが実行されたら私(ViewController)に教えてね!
プロトコルで定義されているメソッドのうちなんのメソッドの処理を依頼するか指定する

①、②、③の情報を整理された図がこちらのサイトでわかりやすくまとめられています。
文字ばかりで説明してしまったので思考の整理ができると思います。ぜひ見てみてください。

猿がもがきまくって理解したSwiftのデリゲート(Delegate)という仕組み
こちらの記事もわかりやすかったです。

最終的には一般的な説明で理解しています。
一般的な説明の補助になれば幸いです。