🍃 앨런 swift 문법 마스터 스쿨 수업을 듣고 제가 이해한대로 정리해서 올리는 포스팅입니다.
- 속성 (property) : 구조체 / 클래스의 변수
(클래스, 구조체, 열거형과 연관되어 있는 정보 / 값 등을 의미)struct Bird { var name: String var weight: Double }
- 값을 저장하기 위해 선언되는 상수/변수
- 구조체 / 클래스 (열거형X)
struct Bird { var name: String var weight: Double init(name: String, weight: Double) { self.name = name self.weight = weight } func fly() { print("날아갑니다.") } }
- Bird라는 클래스에 저장된 name이란 변수, weight라는 변수 모두 저장 프로퍼티
- 클래스 / 구조체의 틀에서 찍어낸 각 인스턴스가 가지는 고유의 데이터 저장 공간 (고유 메모리 공간)
- 변수(var)나 상수(let)로 선언 가능
- 객체의 초기화 시, 각 저장 속성은 반드시 값을 가져야 한다. 즉, 초기화 이전에 값을 가지고 있거나, 생성자 메서드를 통해 값을 반드시 초기화 해야 함
- 기본값 설정하거나 또는 생성자에서 설정, 또는 옵셔널 타입으로 선언하여 nil을 초기값으로 갖는 것 가능
- 해당 저장 속성의 초기화를 지연시키는 것
- 즉, 인스턴스가 초기화되는 시점에 해당 속성이 값을 가지고 초기화 되는 것이 아니라 (메모리에 공간과 값을 갖는 것이 아니라)
해당 속성(변수)에 접근하는 순간에 개별적으로 초기화 하는 것- 따라서, 상수로의 선언은 안되고 변수로의 선언만 가능 (lazy var)
struct Bird { var name: String lazy var weight: Double = 0.2 init(name: String) { self.name = name } func fly() { print("날아갑니다.") } } var aBird = Bird(name:"새")
- weight 라는 속성은 초기화 시점에 메모리 공간이 생기는 것이 아님
✨ 주의점 - 지연 저장 속성은 선언시점에 기본값을 저장, 구조체와 클래스 동일 - 저장하려는 속성과 리턴형만 일치한다면 함수 호출코드, 계산코드, 클로저 코드 등도 모두 가능
✨ 사용하는 이유 1) 메모리를 많이 차지할 때 2) 다른 저장 속성에 의존해야 할 때
- 구조체 / 클래스 / (열거형)
class Person { var name = "사람" var h = 160.0 var w = 45.0 var bmi: Double { get { return w / (h*h) * 10000 } set { w = newValue * h * h / 10000 } } } var p = Person() p.bmi // get ➡️ 밖에서 접근해서 값을 얻기 p.bmi = 23.4 // set ➡️ 밖에서 값을 세팅하기
1) getter (get블록) : 값을 얻기 위한 것
2) setter (set블록) : 값을 세팅하기 위한 것 (선택적 구현)
- 속성의 형태를 가진 실질적 메서드
- 메서드이기에 인스턴스에 메모리 공간이 할당되어있지 않음
- var 로만 선언 가능, 자료형까지 선언해야 함 (타입 추론 안됨)
➡️ 어떠한 값을 저장하는 것이 아니기 때문에 타입 추론을 통해 형식을 알 수 없기 때문
따라서, 내가 어떤 타입의 값을 받아서 다른 저장 프로퍼티에 저장할 것인지,
어떤 타입의 값을 리턴할 것인지를 명시에 주어야 한다- get 블록만 선언하면 읽기전용 (read-only) 계산속성이 된다 (필수구현)
- set 블록은 선택적 구현 (set만 구현은 불가)
- set 블록에서 기본 파라미터 newValue가 제공됨 (직접 파라미터 이름 설정도 가능)
예시
class Person {
var birth: Int = 0 // let안됨! (값이 고정되므로)
var age: Int {
get {
return 2021 - birth
}
set {
// 위에 타입을 정의해 놓았으므로 타입정의 안해도 됨
self.birth = 2021 - newValue
}
}
}
var p = Person()
p.birth = 2000
p.age //get
p.age = 20 //set
p.birth
- birth와 같이 읽거나 쓸 수 있는 저장속성이 먼저 존재해야 하고,
계산속성에서는 이 다른 "저장속성"의 값을 읽거나 쓰는 작업을 해야 한다.
- set의 파라미터 이름을 짓기 힘들다면 get처럼 파라미트 받는 부분을
날리고, newValue 라는 이름으로 접근하면 됨
1) 저장 타입 속성
2) 계산 타입 속성
- 저장 & 계산 속성 앞에 "static"이라는 키워드만 붙이면 저장 타입속성, 계산 타입 속성이 되는 것
- 인스턴스에 속한 속성이 아니고, 타입자체에 속한 속성이기에 내/외부에서 Type.property로 접근해야함
(인스턴스가 생성된다고 타입속성이 매번 생성되는 것이 아니라, 호출했을 때 메모리에 한 번 올라가고
그 뒤로는 어디서든 해당 속성을 <공유> 하는 형태)- 저장 타입 속성을 주로 사용
- static (고정적인/고정된) 이라는 키워드를 추가한 저장 속성
class Dog { static let species: String = "Dog" var name: String var weight: Double init (name: String, weight: Double) { self.name = name self.weight = weight } } let dog = Dog(name: "초코", weight: 15.0) dog.name dog.weight Dog.species // Dog(반드시 타입(형식)의 이름으로 접근해야 함)
class Circle { static let pi = 3.141626 static var count = 0 ... }
- 모든 인스턴스가 동일하게 가져야 하는 보편적인 속성이거나, 모든 인스턴스가 공유해야 하는 성격에 가까운 저장속성을 저장 타입 속성으로 선언 - static 키워드 사용. (상속시) 재정의 불가 (메서드만 상속 가능 - class 키워드 사용 불가) - let / var
class Circle { static let pi = 3.141526 ... static var multiPi: Double { return pi * 2 } }
- (상속 시) 재정의 가능 (class 키워드 사용 시에만) - static 또는 class 키워드 사용 (static 상속 시 재정의 불가/class 상속 시 재정의 가능) - var 키워드만 사용 가능 (계산 타입 속성) - 메서드이기 때문에 타입에 메모리 공간이 할당되어 있지 않음 (계산 속성)
✨ (저장) 속성 감시자
1) willSet : 값이 저장되기 직전에 호출된다
2) didSet : 새 값이 저장된 직후에 호출된다
- 속성 값의 변화를 관찰하는 것으로, 저장 속성에 추가할 수 있다
- (저장) 속성 감시자 ➡️ 저장 속성을 감시한다, 관찰한다 👀 (성격은 메서드)
- 내가 관찰하는 속성에 누가 값을 설정하려고 한다? 🚨 프로퍼티 변경 감지하며 알람
- 구조체 / 클래스 / (열거형)
var name: String = "Unknown" { willSet(newName) { print("현재 이름 = \(name), 바꿀 이름 = \(newName)") } }
✨ willSet 옵저버는 저장속성에 위와 같이 구현한다.
값이 저장되기 <직전> 에 새로 저장될 값이 <파라미터> 로 전달된다
값이 변경되기 전에 호출되는것이라 현재 name 은 아직 Unkown 값
newName이라는 파라미터 이름을 내가 직접 지정해서 위처럼 사용할 수 있으나
newValue의 사용 역시 가능 ⬇️var name: String = "Unknown" { willSet { print("현재 이름 = \(name), 바꿀 이름 = \(newValue)") } }
var name: String = "Unknown" { didSet(oldName) { print("현재 이름 = \(name), 바뀌기 전 이름 = \(oldName)") } }
✨ didSet 옵저버는 저장속성에 위와 같이 구현한다.
값이 저장된 <직후> 에 이전 속성의 값이 파라미터로 전달
그래서 name은 변경 된 값, 이전에 저장되어 있던 Unknown값이
파라미터 oldValue로 넘어온 것
oldName이라는 파라미터 이름을 내가 직접 지정해서 위처럼 사용할 수 있으나
oldValue의 사용 역시 가능 ⬇️var name: String = "Unknown" { didSet { print("현재 이름 = \(name), 바뀌기 전 이름 = \(oldValue)") } }
class Profile { ... var sMessage: String = "기본메시지" { willSet { print("\(sMessage)=>\(newValue)") // (값이 변할 때) 새 값이 저장되기 직전에 호출한다 } didSet { print("\(oldValue)=>\(sMessage)") // (값이 변할 때) 새 값이 저장되기 직후에 호출한다 } } ... }
- var 로만 선언 가능 (let은 관찰할 필요 없음) - 자료형 선언은 일반 변수와 동일 (형식 추론 가능) - 변수가 변하면, (무엇인가) 업데이트 하려는 패턴을 구현할 때 사용 ex) 상태메시지, 프로필 사진 서버에서의 변화 ➡️ (바로) 화면 업데이트