값타입 - 값을 복사
참조타입 - 주소값을 전달
선택의 기준?
어디서나 원본을 수정할 수 있어야 하는가? → 클래스
어디서 수정해도 원본은 영향을 받지 않게 해야하는가? → 구조체
let으로 선언시
참조타입은 인스턴스를 let으로 선언해도 속성을 바꿀 수 있다. (왜? 값을 가리키고있는 주소를 전달해서 값은 바꾸기 가능)
값타입은 인스턴스를 let으로 선언하면 속성을 바꿀 수 없다. (왜? 값 자체를 전달해서 값을 못바꿈)
구조체
== 만 가능
struct Point: Equatable { // 구조체는 Equatable 프로토콜 구체적구현은 안해줘도됨(자동)
var x: Int
var y: Int
}
var myHome: Point = Point(x: 50, y: 50)
var codaHome = myHome
codaHome.y = 150
print(myHome == codaHome) //false: 값을 복사해서 할당해서 codaHome에서 속성변경시 myHome과 값이 달라짐
클래스
== 와 === 둘다 가능
class Person: Equatable {
var name: String
var weight: Int
init(name: String, weight: Int) {
self.name = name
self.weight = weight
}
static func == (lhs: Person, rhs: Person) -> Bool {
return lhs.name == rhs.name && lhs.weight == rhs.weight
//여기서 정의하기 나름으로 값 비교 가능
//이름만 같은가? 비교해볼수도 있음 그럼 몸무게 달라도 true 나오겠지..
}
}
let yagom: Person = Person(name: "yagom", weight: 75)
let coda = yagom
let minime: Person = Person(name: "yagom", weight: 75)
print(coda === yagom) //true: 인스턴스의 주소값을 할당해서 coda나 yagom 이나 동일 인스턴스 가리킴
print(coda == yagom) //true: coda 와 yagom 에게 할당된 값을 비교해봐도 당연히 같음 (같은 주소에 있는 인스턴스의 값을 가져서)
print(minime == yagom) //true: 인스턴스의 주소는 다르지만, 단순히 값은 동일함
print(minime === yagom) //false: 값은 동일하나 다른 인스턴스임
→ 이걸 이해하기 위해서는 값타입 / 참조타입을 이해해야함
private init()
? → 새로운 인스턴스를 못만들게 막아놓은거static let
? → 유일한 인스턴스를 생성해서 타입 프로퍼티에 할당해 어디서든 접근가능하게프로젝트 모듈 안에서 딱 하나만 존재해야할 필요가 있는 경우
디자인패턴중 하나
이 타입으로부터 단 하나의 인스턴스만 존재해야할 때 싱글턴을 사용해라
ex) 앱을 사용하고 있는 사용자정보
: 왜 구조체는 안돼?
이 앱에 하나만 존재해야하는데 값타입이라면 다른곳에서 복사를 해갈 수 있으니
class AccountInfo {
//인스턴스 프로퍼티
var id: String = ""
var password: String = ""
//타입 프로퍼티
static let shared: AccountInfo = AccountInfo() //본인의 인스턴스를 하나 만들어서 그것의 주소값을 담고있음 -> 이렇게 잘 사용하진 않음
private init() {}
}
//shared는 타입 프로퍼티라 AccountInfo().shared 가 아니라
let sharedAccountInfo = AccountInfo.shared //만들어놓은 단 하나의 인스턴스를 불러온거임
shared 의 기본값은 AccountInfo 의 주소값을 들고 있는거고, let이기 때문에 값이 변하지 않음
몇번을 호출해도 같은 값일 것이다.
- 싱글턴은 앱 생명주기 내 **한번이라도 호출** 되어야 **인스턴스를 생성**한다.
- 호출 == 뷰컨트롤러 안에서 호출되어야함
class RegistrantManager {
static let shared = RegistrantManager()
init() {
notificationCenter.addObserver(self, selector: #selector(addResistrant), name: didRegistrantInfo, object: nil)
}
}
.default
선언post
: 노티피케이션 센터에게 노티요청@objc
키워드 붙여서 방송 들으면 실행할 함수 선언addObserver
로 노티피케이션 센터 구독신청하기import UIKit
//1st 어떤 방송을 때릴지
let didPressButton: Notification.Name = Notification.Name("버튼누름")
let notificationCenter: NotificationCenter = .default
// Model
// 정보가 바뀌지 않으니 구조체가 나음 (클래스보다)
struct Registrant {
let name: String
let phoneNumber: String
}
class ViewController: UIViewController {
@IBOutlet weak var nameTextField: UITextField!
@IBOutlet weak var phoneNumberTextField: UITextField!
@IBOutlet weak var nameLabel: UILabel!
@IBOutlet weak var phoneNumberLabel: UILabel!
// Model
var registrantList: [Registrant] = []
override func viewDidLoad() {
super.viewDidLoad()
//구독신청 4???
notificationCenter.addObserver(self,
selector: #selector(showInfo), //[name]방송을 들으면 [selector]를 실행하겠다!
name: didPressButton,
object: nil)
}
//controller
@IBAction func hitRegisterButton(_ sender: Any) {
register()
//view
nameTextField.text = ""
phoneNumberTextField.text = ""
}
@IBAction func hitCheckButton(_ sender: Any) {
}
@objc
func showInfo() {
//방송을 듣고나서 해야할 일 3
let currentIndex = registrantList.count - 1
nameLabel.text = registrantList[currentIndex].name
phoneNumberLabel.text = registrantList[currentIndex].phoneNumber
}
func register() {
guard let nameText = nameTextField.text else {
return
}
guard let phoneNumberText = phoneNumberTextField.text else {
return
}
let personalInfo = Registrant(name: nameText, phoneNumber: phoneNumberText)
registrantList.append(personalInfo)
// 배열에 연락처 추가한 후에 방송해달라고 방송국에게 요청 2
notificationCenter.post(name: didPressButton, object: nil)
}
}