7/27 수요일
사실 프로퍼티 옵저버가 제일 .. 어떻게 언제 써먹어야 하는지 이해가 안간다...
변경 후 작업을 수행해서 UI를 업데이트하거나 변경 내용을 저장하는 didSet을 주로 사용한다고 한다..
willSet은 사실 많이 사용하진 않고, 속성이 변경되기 전에 프로그램 상태를 알아야 할 때 사용한다..
Property Observer
프로퍼티 옵저버, 말 그대로 저장 프로퍼티에서 사용되고, 값을 관찰하다가 변경되었을 때 호출된다.
didSet, willSet이 있는데
willSet - 변경되기 직전에
didSet - 변경되고난 직후에
구조체에 dream이라는 인스턴스 저장 프로퍼티에서 사용되고 있는 걸 볼 수 있다.
초기값을 Designer라고 준 상태에서
해당 프로퍼티에 접근하기 위해 새 인스턴스를 생성해 구조체를 초기화해서 iOS Developer라는 새로운 값(newValue)을 줬다.
사실 출력값만 보면 정확히 언제 어느 시점에 willSet을 써야 하는지, didSet을 써야 하는지 모르겠다..;
근데 보통 인스턴스를 생성해서 새로운 값을 준 후에 그 객체를 주로 사용하기 때문에 didSet을 많이 활용하는 것이 아닐까라고 추측해봄..
사실 프로퍼티 속성이 바뀌면 일반적으로 그 부분을 적용하는 함수를 호출해주면 되는데 왜 프로퍼티 옵저버가 있냐? 의아하다..
그래서 좀.. 구글링해봤는데,,,
장점이 편의성이라고 한다.
프로퍼티 옵저버를 사용하면 속성이 변경될 때마다 기능이 실행된다.
만약에 버튼을 누를 때마다 글자색이 바뀌고 값이 더해져야 하고, 스위치값이 바뀌고,, 어쩌구저쩌구하는 함수를 호출해야 하는데
함수 호출하는 걸 까먹으면 큰일난다..
근데 didSet을 통해서 저장 프로퍼티의 값이 변경된 직후에 해당 프로퍼티에게 어떤 기능을 주겠다!라는
동작코드를 실행하는 기능을 주면 굉장히 편리하다!
위와 다르게 구조체 내에서 초기화가 안되어 있지만 인스턴스를 생성해 구조체를 초기화해주고 있기 때문에 이것 또한 위와 같이 출력된다.
* 추가로 newValue 대신에 따로 파라미터를 지정해줄 수 있다.
willSet (newDream) 과 같이 작성해주면 newDream을 출력문 내에 써주면 된다.
실제 앱 프로젝트에서 사용되는 경우를 보면
https://sujinnaljin.medium.com/swift-class와-struct에서-didset의-차이-f784e34ea33f
Class
Outlet으로 연결한 TextField의 프로퍼티 옵저버에 초기 UI 코드를 때려박았다.
구럼,, 매번 저 텍스트필드를 사용할 때마다 저 UI 코드가 동작하는가? -> NO!
궁금한 포인트는 왜 매번 호출되는 게 아니라 한 번만 동작하는가이다! - 이 부분은 더 찾아봐야겠음..
왤까? 그 이유는 Outlet 프로퍼티는 참조타입에 속한다.
우선, UIKit에 속하고, Class에 속하는 아이들이기에 참조타입에 속한다.
클래스의 가장 큰 특징은 같은 힙 영역에 메모리 주소와 실질적인 데이터가 올라간다.
그렇기 때문에 같은 공간에 값이 올라가기 때문에 하나의 값이 바뀌면 다른 인스턴스들의 값도 다 바뀌게 된다.
이 부분이 이해가 안가네... 그래서 왜? 레퍼런스 타입이어서? 왜?
그러니까, textField 자체를 바꾼 게 아니라 textField.textColor를 바꿨기 때문에 아무 일도 일어나지 않는 것이다.
@IBOutlet 프로퍼티는 클래스가 막 초기화될 때는 nil 상태이다. 그리고 viewDidLoad 실행 전에 nil인지 아닌지 알 수 있다.
Struct
구조체는 값타입이고 복사본이다. 스택영역에 메모리 주소를 복사한다고 한다.
값을 복사해서 기존 인스턴스와 구분해서 스택에 저장하기 때문에 하나의 값을 바꿔도 다른 값이 달라지는 것은 아니다.
list라는 ToDo 구조체 타입의 배열에 값이 추가되거나, 삭제되어 변경사항이 생기면
didSet 프로퍼티 옵저버에 의해서 tableView가 갱신된다.
그래서 매번 추가/삭제될 때마다 reloadData를 해주지 않아도 된다.
별개로, 테이블뷰에서 cell 내에 버튼을 선택하는 코드인데 재사용셀의 문제점을 해결하는 지점에서
sender.tag를 통해서 indexPath.row를 전달받을 수 있다는 점!
cellForRowAt에서
cell.checkButton.tag = indexPath.row
체크버튼의 tag에 indexPath.row를 주고
Todo라는 구조체를 하나 만들어준다.
구조체에는 영화 제목이랑 / 영화를 봤는지에 대한 여부와 관련된 Bool 타입을 갖는 프로퍼티를 만든다.
list 프로퍼티를 하나 생성해서 더미를 대강 쌓고
cellForRowAt 메소드에서
let value = list[indexPath.row].done ? "checkmark.circle.fill" : "checkmark.circle"
cell.checkButton.setImage(UIImage(systemName: value), for: .normal)
cell.checkButton.addTarget(self, action: #selector(touchupCheckButton(_:)), for: .touchUpInside)
만약 done == true인 경우와 false인 경우 버튼의 이미지를 분기처리해준다.
그리고 addTarget 함수를 처리해주는데
해당 함수 내에서는
해당 배열의 인덱스 즉, list 배열에서 sender.tag를 통해 indexPath.row를 받아오고, done을 바꿔주면 된다.
tableView.reloadRows를 통해 해당 cell 영역만 갱신해준다.
'⭐️ 개발 > iOS & Swift' 카테고리의 다른 글
[iOS] 프로토콜 (0) | 2022.07.28 |
---|---|
[iOS] '처음으로' 돌아가는 코드 | SceneDelegate에서 분기처리해주는 코드 (0) | 2022.07.27 |
[Swift] 타입/인스턴스 - 저장/연산 프로퍼티 (0) | 2022.07.27 |
[Swift] 타입 프로퍼티 왜 씀? (0) | 2022.07.21 |
[iOS] cell shadow + cornerRadius 같이 주는 방법 (0) | 2022.07.21 |