클래스나 구조체 안의 변수나 상수를 흔히 프로퍼티라고 칭함.
struct FixedRadiusCircle {
var x: Int // 중심의 x 좌표
var y: Int // 중심의 y 좌표
let radius: Int // 반지름
}
var tenCircle = FixedRadiusCircle(x: 0, y: 0, radius: 10)
tenCircle.x = 3
print(tenCircle) // FixedRadiusCircle(x: 3, y: 0, radius: 10)
변수로 선언한 구조체는 프로퍼티의 값을 변경할 수 있다.
🚨 에러
tenCircle.radius = 3
// Cannot assign to property: 'radius' is a 'let' constant
구조체를 변수로 선언했지만 프로퍼티가 let으로 선언되었으므로 에러
🚨 에러
let threeCircle = FixedRadiusCircle(x: 0, y: 0, radius: 3)
threeCircle.x = 3
// Cannot assign to property: 'threeCircle' is a 'let' constant
구조체를 상수로 선언했다면 구조체 인스턴스의 프로퍼티는 변경할 수 없다.
이름 그대로 해석해 보자!
lazy
키워드 붙이기var
) 선언하기지연 프로퍼티를 사용하면 좋은 점?
ex)
<class Test {
init() {
print("언제 실행될까요?")
}
}
class Min {
var test = Test()
}
let min = Min()
// 언제 실행될까요?
<class Test {
init() {
print("언제 실행될까요?")
}
}
class Min {
lazy var test = Test()
}
let min = Min()
아무것도 실행되지 않음. 이유는 당연히 지연 저장 프로퍼티(test
)에 접근하지 않았으니 아직 초기화가 되지 않았다!
class Test {
init() {
print("언제 실행될까요?")
}
}
class Min {
lazy var test = Test()
}
let min = Min()
min.test // 언제 실행될까요?
lazy
수식어가 표시된 프로퍼티는 여러 쓰레드에서 동시에 접근되고,
프로퍼티가 아직 초기화되지 않은 경우 프로퍼티가 한번만 초기화 된다는 보장이 없습니다.
- The Swift Programming Language
getter
와 setter
로 값을 탐색하고 간접적으로 다른 프로퍼티 값을 설정할 수 있는 방법 제공var
로 선언해야 함상수는 초기화 되기 전에 값이 있어야 하는 속성을 가짐
newValue
)가 존재newValue
라는 이름에서도 알 수 있듯, 해당 프로퍼티에 값이 주어졌을 때 이를 newValue
로 칭하여 연산에 활용함newValue
로만 사용 가능따라서 getter
와 setter
을 사용하기 위해서는 다른 저장 프로퍼티가 반드시 필요하다!
ex)
struct Student {
// 저장 프로퍼티
var name: String
var koreanAge: Int
// 연산 프로퍼티
var westernAge: Int {
get {
let age = koreanAge - 1
return age
}
set(newValue) {
koreanAge = newValue + 1
}
}
}
저장 프로퍼티로 name, koreanAge를, 연산 프로퍼티로 westernAge를 선언한 Student
구조체를 만들었다.
get
- koreanAge
프로퍼티를 이용하여 연산한 값을 return
set
- koreanAge
프로퍼티에 값을 설정
var student = Student(name: "min", koreanAge: 24)
student.westernAge // 23
student.westernAge = 25
student.koreanAge // 26
name
을 min, koreanAge
를 24로 초기화한 student를 선언했다.
이때 westernAge
는 get이 리턴하는 koreanAge - 1 이다.
westernAge
를 25로 변경한 뒤, koreanAge
를 출력하면 26이 된다.
var westernAge: Int {
get {
// return koreanAge - 1
koreanAge - 1
}
set(newValue) {
koreanAge = newValue + 1
}
}
// 연산 프로퍼티
var westernAge: Int {
get {
let age = koreanAge - 1
return age
}
set(newWesternAge) {
koreanAge = newWesternAge + 1
}
}
newValue
만 이용 가능 var westernAge: Int {
get {
let age = koreanAge - 1
return age
}
set {
koreanAge = newValue + 1
}
}
연산 프로퍼티를 사용하지 않고, 위와 동일한 작업을 하려면 메서드 2개를 통해 구현해야 함
func setWesternAge() -> Int {
return koreanAge - 1
}
mutating func setNewKoreanAge(_ westernAge: Int) {
koreanAge = westernAge + 1
}
getter
와 setter
을 통해 표현하는 것이 더 간결하고 가독성이 좋은 것 같다.getter
만 제공되고, setter
은 제공하지 않는 프로퍼티getter
만 제공된다: 연산을 통한 return 값만 제공struct Cuboid {
var width = 0.0, height = 0.0, depth = 0.0
// read-only
var volume: Double {
return width * height * depth
}
}
let fourByFiveByTwo = Cuboid(width: 4.0, height: 5.0, depth: 2.0)
print("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)")
// the volume of fourByFiveByTwo is 40.0
인스턴스가 생성됐을 때 타입 프로퍼티가 매번 생성되는 게 아니라, 호출했을 때 한번 메모리에 올라간 뒤로는 해당 프로퍼티를 공유
따라서 인스턴스 생성 시점의 초기화와 관련이 없어, 초기값을 따로 생성할 수 없기에 초기값 지정이 필요함
(호출한다는 것은 지연 저장 프로퍼티처럼 접근했을 때를 의미함.)
static
키워드 붙이기class
타입 연산 프로퍼티의 경우 class
키워드를 붙여 서브 클래스에서 오버라이딩할 수 있음타입 프로퍼티에 접근 시에는 인스턴스를 생성하여 접근하는 것이 아닌 타입 자체에 접근해야 함
class TestClass {
static let storedTypeProperty = "hi"
static var storedTypeProperty2 = "hi2"
static var computedTypeProperty: Int {
return 6
}
}
print(TestClass.storedTypeProperty) // hi
TestClass.storedTypeProperty2 = "hi2 is gone!"
print(TestClass.storedTypeProperty2) // hi2 is gone!
print(TestClass.computedTypeProperty) // 6
class Vehicle {
class var name: String {
return "Vehicle"
}
}
class Car: Vehicle {
override class var name: String {
return "Car"
}
}
Vehicle.name // "Vehicle"
Car.name // "Car"
observer: 관찰자 → 프로퍼티의 값이 변경되는지 관찰하고 응답
새 값이 설정(set) 될 때마다 이 이벤트를 감지할 수 있는 옵저버 제공
새 값이 이전과 같더라도 호출됨
서브 클래스의 프로퍼티에 옵저버 정의할 수 있음
지연 저장 프로퍼티에서는 사용할 수 없음
원래는 안 됐었는데, Swift 5.3부터는 가능해짐
class TestClass {
lazy var test: Int = 0 {
willSet {
print("willSet called, newValue:", newValue)
}
didSet {
print("didSet called!, oldValue:", oldValue)
}
}
}
let testClass = TestClass()
testClass.test = 3
/*
willSet called, newValue: 3
didSet called!, oldValue: 0
*/
연산 프로퍼티는 setter
에서 값의 변화를 감지하기 때문에 옵저버를 따로 정의할 필요 없음
willSet
newValue
존재newValue
사용didSet
oldValue
존재파라미터명 지정하지 않으면 oldValue
사용
ex)
class WeightArchive {
var myWeight: Int = 0 {
willSet {
print("Today: \(newValue)kg ")
}
didSet {
print("YesterDay: \(oldValue)kg")
}
}
}
몸무게를 기록하고 이전 몸무게와 현재 몸무게를 확인할 수 있는 class를 구현했다.
let weightArchive = WeightArchive()
weightArchive.myWeight = 55
// Today: 55kg
// YesterDay: 0kg
새 값인 55와 이전 값인 0이 출력되는 것을 알 수 있다.
weightArchive.myWeight = 53
// Today: 53kg
// YesterDay: 55kg
새 값인 53과 이전 값인 55가 출력되는 것을 알 수 있다.
class WeightArchive {
var myWeight: Int = 0 {
willSet(todayWeight) {
print("Today: \(todayWeight)kg ")
}
didSet(yesterDayWeight) {
print("YesterDay: \(yesterDayWeight)kg")
}
}
}
파라미터 명을 다음과 같이 변경할 수도 있다.
https://yagom.github.io/swift_basic/contents/13_property/
https://babbab2.tistory.com/120