🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

1回答

1408閲覧

delegateの理解が正しいか見てほしい

momokoko

総合スコア38

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

0クリップ

投稿2021/02/13 04:45

編集2021/02/13 15:37

子に設定されているdelegateについて以下の自分の認識が合っているか見てほしいです。

前提:
LoginController:子 MainTabController:親
・画像のself.delegate?.authenticationDidComplete()は子のLoginControllerに設定されているコード

自分の認識:
・以下の画像のself.delegate?.authenticationDidComplete()MainTabControllerにあるauthenticationDidCompleteメソッドを呼び出しいる。

イメージ説明

//MainTabController extension MainTabController: AuthenticationDelegate { func authenticationDidComplete() { fetchUser() self.dismiss(animated: true, completion: nil) } }

全体のコード

// // LoginController.swift // InstagramFirestoreTutorial import UIKit protocol AuthenticationDelegate:class { func authenticationDidComplete() } class LoginController: UIViewController { // MARK: - Properties private var viewModel = loginViewModel() // delegateを設定 weak var delegate: AuthenticationDelegate? private let iconImage: UIImageView = { let iv = UIImageView(image: #imageLiteral(resourceName: "Instagram_logo_white") ) iv.contentMode = .scaleAspectFill return iv }() private let emailTextField: CustomTextField = { //CustomTextField クラスのplaceholder: Stringの中にEmailが入っているのか。 let tf = CustomTextField(placeholder: "Email") tf.keyboardType = .emailAddress return tf }() private let passwordTextField: CustomTextField = { let tf = CustomTextField(placeholder: "password") tf.isSecureTextEntry = true return tf }() private let loginButton: CustomUIButton = { let button = CustomUIButton(title: "loginButton") button.isEnabled = false button.addTarget(self, action: #selector(handleLogin), for: .touchUpInside) return button }() private let forgotPasswordButton: UIButton = { let button = UIButton(type: .system) button.attributedTitle(firstPart: "Forgot you password? ", secondPart: "Get help signiing in.") return button }() private let dontHaveAccountButton: UIButton = { let button = UIButton(type: .system) button.attributedTitle(firstPart: "Don't have an account", secondPart: "Sign Up") button.addTarget(self, action: #selector(handleShowSignUp), for: .touchUpInside) return button }() // MARK: - Lifecycle override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .systemPink configureUI() configureNotificationObservers() } // // override func viewDidAppear(_ animated: Bool) { // if // } // // MARK: - Actions @objc func handleLogin() { guard let email = emailTextField.text else { return } guard let password = passwordTextField.text else { return } AuthService.logUserIn(withEmail: email, password: password) { (result, error) in if let error = error { print("ログインエラー: (error.localizedDescription)") return } } self.delegate?.authenticationDidComplete() } @objc func handleShowSignUp() { // インスタンス化 let controller = RegistrationController() // controller.delegate = delegate navigationController?.pushViewController(controller, animated: true) } @objc func textDidChange(sender: UITextField) { if sender == emailTextField{ viewModel.email = sender.text } else { viewModel.password = sender.text } updateForm() } func configureUI() { configureGradientLayer() navigationController?.navigationBar.isHidden = true navigationController?.navigationBar.barStyle = .black view.addSubview(iconImage) iconImage.centerX(inView: view) iconImage.setDimensions(height: 80, width: 120) iconImage.anchor(top: view.safeAreaLayoutGuide.topAnchor,paddingTop: 32) let stack = UIStackView(arrangedSubviews: [emailTextField,passwordTextField,loginButton,forgotPasswordButton]) stack.axis = .vertical stack.spacing = 20 view.addSubview(stack) stack.anchor(top: iconImage.bottomAnchor, left: view.leftAnchor, right: view.rightAnchor, paddingTop: 32, paddingLeft: 32, paddingRight: 32) view.addSubview(dontHaveAccountButton) dontHaveAccountButton.centerX(inView: view) dontHaveAccountButton.anchor(bottom: view.safeAreaLayoutGuide.bottomAnchor) } func configureNotificationObservers() { emailTextField.addTarget(self, action: #selector(textDidChange), for: .editingChanged) passwordTextField.addTarget(self, action: #selector(textDidChange), for: .editingChanged) } } // MARK: - FormViewModel extension LoginController: FormViewModel { func updateForm() { loginButton.backgroundColor = viewModel.buttonBackgroundColor loginButton.setTitleColor(viewModel.buttonTitleColor, for: .normal) loginButton.isEnabled = viewModel.formIsValid } }

MainTabController内でdelegateで検索すると下記のものがありました。

// MainTabController.swift func checkIfUserIsLoggedIn() { if Auth.auth().currentUser == nil { print("現在のユーザーは:(Auth.auth().currentUser)です") DispatchQueue.main.async { let controller = LoginController() // 子に設定されている(LoginController())を自身にもセット controller.delegate = self let nav = UINavigationController(rootViewController: controller) nav.modalPresentationStyle = .fullScreen self.present(nav, animated: true, completion: nil) } }

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

TsukubaDepot

2021/02/13 10:47

delegateとして設定されたインスタンスが移譲先(delegteの言葉そのもの)になるのですが、その設定はどのようになっているのでしょうか。
momokoko

2021/02/13 11:02

こちらにもコメントありがとうございます! ちゃんと質問の答えになっているか不安ですが、 ---------------------------------------------------------------------------------------------------- // LoginController protocol AuthenticationDelegate:class { func authenticationDidComplete() } class LoginController: UIViewController { weak var delegate: AuthenticationDelegate? ----------------------------------------------------------------------------------------------------
momokoko

2021/02/13 11:03

上記のようになっています。
TsukubaDepot

2021/02/13 11:04

どこかで、 delegate = (インスタンス名) という宣言があると思いますが、それはどうなっているのでしょうか。
momokoko

2021/02/13 11:34

再度のご質問ありがとうございます。 そのような表記されたコードはLoginControllerファイル内になかったです。 同じような意味合いを持つコードはあるのかもしれないですが、自分の理解の範囲では見つけることができませんでした。
TsukubaDepot

2021/02/13 11:46

LoginController をインスタンス化しているコードが別のクラスにあると思いますが、それは発見できないでしょうか。 別の言い方をすると、LoginController を呼び出しているクラスのコード内に、delegate の設定があると思います(MainTabControllerのはずです)。 まずはそれを探してみてもらえますでしょうか。
momokoko

2021/02/13 15:36

ありがとうございます。 質問に修正を加えました。 ただ、別の質問でご指摘頂いたように「講座全体のコードのごく一部から推測」になってしまいそうだと思いました。 controller.delegate = selfは、self.delegate?.authenticationDidComplete()と紐付いていないコードと思えるからです。 ちゃんとした理論ではないと思うのですが、 ・controller.delegate = selfをコメントアウトしてもビルドできてしまうこと ・先生がだいぶ後になってcontroller.delegate = selfを書いていたこと です。
hoshi-takanori

2021/02/13 22:03

横から失礼します。想像ですが、udemy のその講座は、delegate とは何なのか、どうやって使うのかはある程度わかってる人向けに、実際のアプリでどんなふうに使われているかを示すものなのでしょうね。 > controller.delegate = selfは、self.delegate?.authenticationDidComplete()と紐付いていないコードと思えるからです。 あらかじめ delegate に値をセットしておき、後でその値を利用する、という一連の流れが「紐付いていない」と感じてしまうのであれば、その講座はまだ早いのでしょう。 > ・controller.delegate = selfをコメントアウトしてもビルドできてしまうこと ビルドできるのと、ちゃんと動くのは違います。 > ・先生がだいぶ後になってcontroller.delegate = selfを書いていたこと delegate プロパティを宣言した時点で、「これは何かを代入しなきゃいけないはずだけど、どこでやるんだろう?」と疑問を持ち、後で実際に代入する場面で、「あ、なるほど、ここで代入すればいいのか!」と納得する、という流れになってるのでしょう。
TsukubaDepot

2021/02/14 12:20

hoshi-takanoriさん、 まさに私がコメントしようとしていた内容で助かりました(地震の後片付けでそれどころではなかった)。
guest

回答1

0

回答の概要は hoshi-takanoriさんがコメントに書かれた通りですが、一応理由をつけて説明します。

Swift

1class LoginController: UIViewController { 2 // 注釈 3 // delegateを設定 4 weak var delegate: AuthenticationDelegate?

という具合に、delegate はオプショナル型として宣言されています。オプショナル型なので、初期値としては nil が入ります。

一方、実際に Delegate を呼びコードを見てみます。

Swift

1 @objc func handleLogin() { 2 // 注釈 3 self.delegate?.authenticationDidComplete() 4 }

ここで、delegate として指定されたインスタンスが持つauthenticationDidComplete()というメソッドが呼ばれています。

ここで注目してほしいのは、オプショナルチェイン(Optional Chaining)という概念です。

オプショナルチェインというのは、インスタンスを出発点に、最終的なメソッドやプロパティまで . (ピリオド)をたどりながら走査と実行を連結して行う手法の一つで)。

ただし、チェインの途中に? が含まれる場合、その直前で指定されたプロパティの値や、あるいはメソッドの返り値が nil の場合は、そこで走査が打ち切られ、以降に指定された処理はキャンセルされるようになっています。

一方、途中で nil の存在を判定せずに連結する方法をチェイン(チェインメソッド)と呼びます。Xcode の場合は補完機能が優秀なので、途中で nil を返しうる処理が含まれる場合には、自動的にオプショナルチェインの提案を行いますので、あまり意識することはないかもしれません(本当は意識しないといけない)。

なので、

・controller.delegate = selfをコメントアウトしてもビルドできてしまうこと

とありますが、hoshi-takanoriさんご指摘の通り、ここをコメントアウトしてもコンパイルは通ります。

それは例えば、クラス内の初期化済みプロパティ(変数)に別の場所で値を代入しない状態でコンパイルしてもエラーにならないのと同じです。

Swift

1controller.delegate = self

をコメントアウトした場合、controller.delegate の値はクラスで指定した規定値、つまり nil で初期化され、その後上書きされることもありません。

なので、上記コードをコメントアウトした状態でアプリを実行すると、

Swift

1 @objc func handleLogin() { 2 // 注釈 3 self.delegate?.authenticationDidComplete() 4 }

の段階で delegatenil の状態ですので、そこで走査は打ち切られ、以降の authenticationDidComplete() は実行されません。

逆に、上記のコードを指定した場合には、クラスがインスタンス化(Instantiateされる段階)では一度 nil で初期化され、その後 self、つまり delegate を指定しようとしているクラス自身のインスタンスを代入することになります。

・先生がだいぶ後になってcontroller.delegate = selfを書いていたこと

これは、実際に講座を聞いてみないとその意図はわかりませんが、想像できるのは

  • 講師が単に代入操作を忘れていて、その後それに気づいたので後になってコードを追加した
  • 講師は delegate の概念を教えるため、あえて最初は代入しない状態で実行し、その後 self を代入させることで具体的な動作を教えた

のいづれかと思いますが、こればかりは発言をみてみないことにはなんとも言えません。

投稿2021/02/14 12:40

TsukubaDepot

総合スコア5086

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問