THIS IS ELLIE

self생성 전 self를 참조할 때 발생하는 이슈 본문

낑낑/Troubleshooting

self생성 전 self를 참조할 때 발생하는 이슈

Ellie Kim 2021. 10. 15. 17:57

이것도 일하다가 발견한 ^^,,, 신기한 것
(아래 코드는 바로 직전 포스트 한 코드 재탕)

class ViewController: UIViewController {
    private let slider: UISlider = {
        let slider: UISlider = UISlider()
        slider.translatesAutoresizingMaskIntoConstraints = false
        slider.minimumValue = 0
        slider.maximumValue = 100
        slider.value = 0
        slider.addTarget(self, action: #selector(sliderValueChanged(sender:)), for: .valueChanged)
        return slider
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.addSubview(slider)
        
        slider.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 20).isActive = true
        slider.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -20).isActive = true
        slider.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        slider.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
    }
    
    @objc private func sliderValueChanged(sender: UISlider) {
        print(sender.value)
    }
}

위 코드에서 이상한 점 발견하면 천재ㅇㅇ

이상한 점은 let으로 선언해놓고 안에서 addTarget으로 self를 사용하고 있는 것입니다.
let 변수는 self/super.init이 실행되기 전에 값이 주어지기 때문에 self가 존재하지 않습니다.

여기서 슬라이더에 타겟을 등록하기 위해선 self를 참조해야 하므로 self가 존재할 때까지 생성을 지연시켜야 합니다.
만약 let으로 등록하고 싶다면 self시점 이후에 호출되는 메서드에서 addTarget을 호출해주면 됩니다.
하지만 특정 이벤트에만 사용되는 객체면 lazy var를 사용하는 게 더 좋습니다. 
사용하지 않는데 굳이 로드해서 메모리를 사용할 필요가 없기 때문입니다.

class ViewController: UIViewController {
    private lazy var slider: UISlider = {
        let slider: UISlider = UISlider()
        slider.translatesAutoresizingMaskIntoConstraints = false
        slider.minimumValue = 0
        slider.maximumValue = 100
        slider.value = 0
        slider.addTarget(self, action: #selector(sliderValueChanged(sender:)), for: .valueChanged)
        return slider
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.addSubview(slider)
        
        slider.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 20).isActive = true
        slider.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -20).isActive = true
        slider.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        slider.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
    }
    
    @objc private func sliderValueChanged(sender: UISlider) {
        print(sender.value)
    }
}

즉 위와 같이 선언되어야 알맞게 사용되는 코드

초기화할 때 self를 사용하고 싶다면 lazy var를 사용하거나
let으로 선언하고 addTarget은 self가 생성된 후에 사용해야 함


++개인적인 궁금증 (누가 정답 좀 알려줬으면..제발)

근데 예전에는 Xcode가 let에 self 쓰면 워닝을 띄운 것 같은데 아닌가...
(워닝 설정 그대로 되어있음)

근데 더 이상한 점은 안될 것 같은 저 코드가 특정 기기에서는 문제없이 잘 실행이 되고 특정 기기에서는 문제가 발생한다는 것.
특정 기기에선 안되고 특정 기기에서는 잘 되고... 뭐가 달라.


lazy var는 당연히 되어야 하지만, let으로 선언해도 self가 잘 참조되고 있더라구요. 

https://docs.swift.org/swift-book/LanguageGuide/Properties.html NOTE에 보면 Constant properties must always have a value before initialization completes라 적혀있음.

 

Q. let 내부에서 self를 선언해도 잘 되네? 근데 특정 기기는 아예 안되었음.
// 10개 중 2개 정도는 안되고 8개는 되고

된다는 건 let이 self생성후 만들어지는????건데 왜 어떤건 되고 어떤건 안되냐
아직도 답을 못찾음 혹시 아시는 분은 알려주세요.

의심 가는 것
- 스위프트 버전?
- OS 차이?
- ????? 1도 모르겠음

반응형