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

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

詳細はこちら
Swift

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

Q&A

解決済

1回答

760閲覧

lazyなしでエラーにならないのはなぜか。

momokoko

総合スコア38

Swift

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

0グッド

0クリップ

投稿2021/02/21 10:22

編集2021/02/22 12:08

過去、私がした質問を前提知識として質問しています。
リンク内容

上記の質問でlazyを付ける意味が理解できたのですが、なぜ下記のコードはlazyを付けなくてもいいのか知りたいです。

①lazyを付けるべきだと思っている箇所
・点線で囲んだ①

他のプロパティも

// RegistrationController.swift import UIKit class RegistrationController: UIViewController { // MARK: - Properties private var viewModel = RegistrationViewModel() private var profileImage: UIImage? weak var delegate: AuthenticationDelegate? // プッシュしたボタンはなぜ、色が変わるか -----------------------------------①------------------------------------------------- private let plushPhotoButton: UIButton = { let button = UIButton(type: .system) button.setImage(#imageLiteral(resourceName: "plus_photo"), for: .normal) button.tintColor = .white button.addTarget(self, action: #selector(handleProfilePhotosSelect), for: .touchUpInside) return button }() ------------------------------------------------------------------------------------- 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 fullnameTextField = CustomTextField(placeholder: "Fullname") private let usernameTextField = CustomTextField(placeholder: "Username") -----------------------------------①------------------------------------------------- private let signUpButton: CustomUIButton = { let button = CustomUIButton(title: "SignUpButton") button.addTarget(self, action: #selector(handleSignUp), for: .touchUpInside) button.isEnabled = false return button }() private let alreadyHaveAccountButton: UIButton = { let button = UIButton(type: .system) button.attributedTitle(firstPart: "Already have an account", secondPart: "Sign Up") button.addTarget(self, action: #selector(handleShowSignUp), for: .touchUpInside) return button }() -------------------------------------------------------------------------------------- // MARK: - Lifecycle override func viewDidLoad() { super.viewDidLoad() configureUI() configureNotificationObservers() } // MARK: - Actions @objc func handleSignUp() { guard let email = emailTextField.text else { return } guard let password = passwordTextField.text else { return } guard let fullname = fullnameTextField.text else { return } guard let username = usernameTextField.text?.lowercased() else { return } guard let profileImage = self.profileImage else { return } let credentials = AuthCredentials(email: email,password: password, fullname: fullname,username: username, profileImage: profileImage) // AuthService.registerUser(withCredential : credentials) AuthService.registerUser(credentials: credentials) { (error) in if let error = error { print("登録時のエラー RegistrationController 2101162205: (error.localizedDescription)") return } // self.dismiss(animated: true, completion: nil) self.delegate?.authenticationDidComplete() print("現在のViewControllerを破棄=>Feedに戻る") } } @objc func handleShowSignUp() { navigationController?.popViewController(animated: true) print("LoginViewに戻る") } @objc func textDidChange(sender: UITextField) { if sender == emailTextField{ viewModel.email = sender.text print("email開始") print(emailTextField.text) print("email終了") } else if sender == passwordTextField { viewModel.password = sender.text print("password") } else if sender == fullnameTextField { viewModel.fullname = sender.text print("fullname") } else { viewModel.username = sender.text print("username") } print("full") updateForm() } @objc func handleProfilePhotosSelect() { let picker = UIImagePickerController() picker.delegate = self picker.allowsEditing = true present(picker, animated: true, completion: nil) } // MARK: - Helpers func configureUI() { configureGradientLayer() view.addSubview(plushPhotoButton) plushPhotoButton.centerX(inView: view) plushPhotoButton.setDimensions(height: 140, width: 140) plushPhotoButton.anchor(top: view.safeAreaLayoutGuide.topAnchor,paddingTop: 32) let stack = UIStackView(arrangedSubviews: [emailTextField,passwordTextField,fullnameTextField,usernameTextField,signUpButton,alreadyHaveAccountButton]) stack.axis = .vertical stack.spacing = 20 view.addSubview(stack) stack.anchor(top: plushPhotoButton.bottomAnchor,left: view.leftAnchor,right: view.rightAnchor,paddingTop: 32,paddingLeft: 32,paddingRight: 32) view.addSubview(alreadyHaveAccountButton) alreadyHaveAccountButton.centerX(inView: view) // 質問1 safeAreaLayoutGuideとは alreadyHaveAccountButton.anchor(bottom: view.safeAreaLayoutGuide.bottomAnchor) } func configureNotificationObservers() { emailTextField.addTarget(self, action: #selector(textDidChange), for: .editingChanged) passwordTextField.addTarget(self, action: #selector(textDidChange), for: .editingChanged) fullnameTextField.addTarget(self, action: #selector(textDidChange), for: .editingChanged) usernameTextField.addTarget(self, action: #selector(textDidChange), for: .editingChanged) } } // MARK: - FormViewModel extension RegistrationController: FormViewModel { func updateForm() { signUpButton.backgroundColor = viewModel.buttonBackgroundColor signUpButton.setTitleColor(viewModel.buttonTitleColor, for: .normal) signUpButton.isEnabled = viewModel.formIsValid } } // MARK: - UIImagePickerControllerDelegate extension RegistrationController: UIImagePickerControllerDelegate,UINavigationControllerDelegate { // 写真が選択され終わったときに呼ばれる func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { guard let selectedImage = info[.editedImage] as? UIImage else { return } profileImage = selectedImage plushPhotoButton.layer.cornerRadius = plushPhotoButton.frame.width / 2 plushPhotoButton.layer.masksToBounds = true plushPhotoButton.layer.borderColor = UIColor.white.cgColor // plushPhotoButton.setImage(selectedImage, for: .normal) plushPhotoButton.setImage(selectedImage.withRenderingMode(.alwaysOriginal), for: .normal) // let userPhoto = selectedImage.withRenderingMode(.alwaysOriginal) // plushPhotoButton.setImage(userPhoto, for: .normal) self.dismiss(animated: true, completion: nil) } }

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

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

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

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

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

momokoko

2021/02/22 10:20 編集

コメントありがとうございます。 上記のリンクを参考にして整理してみます。
hoshi-takanori

2021/02/22 11:37

というか、signUpButton だけ lazy がないのに違和感がある (他の plushPhotoButton や emailTextField には lazy がなくても気にならないってことですよね?) 理由が分からなかったので、気が向いたら何が引っかかってるかお知らせください。
momokoko

2021/02/22 12:06

すみません(-_-;) 例でsignUpButtonとしただけです。。
momokoko

2021/02/22 12:07

質問に問題がありました。 修正を加えておきます。
hoshi-takanori

2021/02/23 11:05

何が引っかかってるのかをお聞きしたかったのですが、たぶん lazy じゃないのに self が使われていることですよね。で、上の stackoverflow ではこれはコンパイラのバグだろうと言ってます。↓ の Note に You also can’t use the implicit self property とはっきり書いてありますから。 https://docs.swift.org/swift-book/LanguageGuide/Initialization.html#ID232
momokoko

2021/02/23 11:32

コメントありがとうございます。 何が疑問なのか質問文に書いておらず推測までして頂きありがとうございます。 ぼくが疑問だと思っていることは 前回の質問での followersLabelプロパティでlazyを付けた理由が、attributedStatTextメソッドにアクセスするため であったら、 signUpButtonプロパティでもhandleSignUpメソッドにアクセスしているため、lazyを付けた方がいいのではないかと思っていることです。 実際は、付けなくてもエラーが出ないので必要ないと思うのですが、followersLabelプロパティ時との違いはわからなかったので質問させていただきました。
guest

回答1

0

ベストアンサー

ぼくが疑問だと思っていることは
前回の質問での
followersLabelプロパティでlazyを付けた理由が、attributedStatTextメソッドにアクセスするため
であったら、
signUpButtonプロパティでもhandleSignUpメソッドにアクセスしているため、lazyを付けた方がいいのではないかと思っていることです。

前回のコードでは、初期化クロージャで attributedStatText メソッドを呼び出していて、これはインスタンスメソッドなので self に対するメソッド呼び出しになっているため、lazy を付けないとself の初期化が完了してないということでエラーになってました。

(実は、attributedStatText メソッドの中では self のプロパティやメソッドを使ってないので static メソッドにすることができて、そうすると lazy var じゃなくても良かったはず。)

で、今回のコードで気になってるのは、

swift

1button.addTarget(self, action: #selector(handleProfilePhotosSelect), for: .touchUpInside)

#selector(handleProfilePhotosSelect) の部分ですね。これはメソッドセレクターと言って、インスタンスとは切り離してメソッドを参照しているだけなので、たとえインスタンスメソッドであってもセレクター自体は self (または特定のインスタンス) とは無関係であり、lazy でなくても問題ありません。(この参照を使って実際にメソッドを呼び出す時にはインスタンスが必要になりますが。)

とは言え、コメント欄にも書いた通り、初期化の済んでない self を渡していることは Swift 言語仕様に反しているので、これがエラーにならないのはコンパイラのバグではないかと指摘されています。
参考: swift4 - Swift - self in variable initialization closure - Stack Overflow

投稿2021/02/23 11:57

hoshi-takanori

総合スコア7899

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

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

momokoko

2021/02/23 16:22

ありがとうございます。 今は頭が回っていないので後でゆっくり読ませて頂きます。 取り急ぎお礼申し上げます。
momokoko

2021/02/26 14:05

ありがとうございます。 理解できました。 頭の整理のために学んだことを記載させていただきます。 ・lazyを付けるケース:インスタンスメソッド※1にアクセスする場合 ・今回の#selector(handleProfilePhotosSelect)はselfとは無関係のため、selfの初期化に関係なくなる。 ※1 インスタンスメソッド:インスタンスが保持しているメンバー変数を使って何か処理する関数
momokoko

2021/02/26 14:06

たしかに上の理解だと、selfを渡してエラーにならないのはおかしな話ですね。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問