가. 저장프로퍼티
class Human {
let name: String = "joo"
var age: Int = 30
}
struct Person {
let name: String = "joo"
var age: Int = 40
}
let human = Human()
human.name = "another name" // wrong
human.age = 40 // 출력 40
Q. 상수 human으로 인스턴스 생성했는데, 왜 age 프로퍼티가 변환이 가능할까?
a. human은 스택에 할당되고, Human은 힙에 할당이 된다. 스택에 있는 human은 힙의 Human 클래스를 참조하고 있다. 그래서 human 안에 힙에 할당된 클래스의 주소값이 들어가 있다. Human을 상수로 인스턴스 생성하게 되면, 실제 저장된 힙 영역에 저장된 name, age와 상관없이 스택에 저장된 human안의 주소값이 상수가 된다. 그래서 인스턴스 생성당시 변수나 상수로 선언하던지 저장프로퍼티에 접근에 영향을 주지 않는다.
나. 지연 저장 프로퍼티
클래스의 인스턴스 생성과 상관없이 처음 사용될 때, 개별적으로 초기화된다.
항상 변수로 선언되어야 한다.
class Contacts {
var email: [String] = .init(repeating: "", count: 100000)
var address: String = ""
init(){
print("Contacts Init")
}
}
class User{
lazy name: String = "unknown"
lazy var contacts: Contacts = .init()
}
// 만약 지연저장프로퍼티를 사용하지 않으면, 실제 유저 정보가 5,000개였다면 필요없이 100,000개의 정보를 생성하는 것이다.
// 이로써 메모리 낭비를 줄일 수 있게 되는 것이다.
struct BMI {
var nickname: String {
willSet {
print("유저 닉네임이 \(nickname)에서 \(newValue)로 변경될 예정이다.")
}
didSet {
print("유저 닉네임 변경 완료 \(oldValue) -> \(nickname)으로 바뀜")
}
}// 타입프로퍼티는 처음부터 무조건 초기값을 가지고 있어야 한다.
var weight: Double
var height: Double
// 저장 프로퍼티는 메모리를 차지하고 있다. 연산프로퍼티는 메모리를 가지고 있지 않다. 저장 프로퍼티를 활용해 원하는 값을 변화하는 용도로 주로 사용
//읽기전용(read only) 프로퍼티지만 계산 하는 값에 따라 결과가 다달라질 수 있기 대문이다.
var BMIResult: String{
get { // 인스턴스를 통해서도 해결할 수 있다. 계산을 해줄 때 비로소 저장된다. 결국 호출해야 올라온다.
let bmiValue = (weight * height) / height
let bmiStatus = bmiValue < 18.5 ? "저체중" : "정상 이상"
return "\(nickname) - \(bmiValue) - \(bmiStatus)"
}
// set {
// nickname = newValue
// }
}
var sample: String {
return "\(Int.random(in: 1...100))"
}
}
//
var bmi = BMI(nickname: "고래밥", weight: 50, height: 100)
let result = bmi.BMIResult
print(result) //미리 안에서 계산을 처리하고 보여주자
bmi.BMIResult = "울라프" // 오 이렇게 값을 바꿔주면 값이 변환시킬 수 있다. 그러나 상단 set이 생략이 되면 값을 바꿔 줄수 없게 된다.
print(bmi.BMIResult)
타입프로퍼티
static
키워드를 활용해 사용할 수 있다. 단, 오버라이딩을 할 경우 class
를 사용한다.프로퍼티 옵저버
class StepCounter { //totalSteps는 "저장 프로퍼티"입니다!!
var totalSteps: Int = 0 {
willSet {
print("totalSteps을 \(newValue)로 설정하려고 합니다")
}
didSet {
if totalSteps > oldValue {
print("\(totalSteps - oldValue)걸음이 추가되었습니다.")
}
}
}
}
let stepCounter = StepCounter()
stepCounter.totalSteps = 200
/*
totalSteps을 200로 설정하려고 합니다
200걸음이 추가되었습니다.
*/
stepCounter.totalSteps = 360
/*
totalSteps을 360로 설정하려고 합니다
160걸음이 추가되었습니다.
*/
프로토콜은 메소드, 프로퍼티에 대해 정의만 하고, 프로토콜을 채택한 곳에서 구현은 한다.
@objc
protocol ViewPresentableProtocol{
var navigationTitleString: String { get set }
var backgroundColor: UIColor { get }
static var identifier: String { get }
func configureView()
@objc optional func configureLabel()
@objc optional func configureTextField()
}
struct ExchangeRate {
var currencyRate: Double {
willSet {
print("CurrenvyRate willSet - 환율 변동 예정: \(currencyRate) -> \(newValue)")
}
didSet {
print("CurrenvyRate didSet - 환율 변동 완료: \(oldValue) -> \(currencyRate)")
}
}
var USD: Double {
get{
return 1.0
}
}
var KRW: Double {
willSet {
print("USD willSet - 환전금액: USD: \(newValue / currencyRate)달러로 환전될 예정") // 50만원 / 1100을 나눈 것이다.
}
didSet {
print("USD didSet - KRW: \(KRW) -> \(KRW/currencyRate)달러로 환전되었음. ")
}
}
}
var rate = ExchangeRate(currencyRate: 1100, KRW: 1)
rate.KRW = 500000.0
//USD willSet - 환전금액: USD: 454.54545454545456달러로 환전될 예정
//USD didSet - KRW: 500000.0 -> 454.54545454545456달러로 환전되었음.
rate.currencyRate = 1350.0
//CurrenvyRate willSet - 환율 변동 예정: 1100.0 -> 1350.0
//CurrenvyRate didSet - 환율 변동 완료: 1100.0 -> 1350.0
rate.KRW = 500000.0
//USD willSet - 환전금액: USD: 370.3703703703704달러로 환전될 예정
//USD didSet - KRW: 500000.0 -> 370.3703703703704달러로 환전되었음.