Property
는 value를 특정 클래스, 구조체, 또는 열거형과 연관시킨다.
Stored Property(저장 프로퍼티)
는 인스턴스의 일부로 상수나 변수의 값을 저장하고,
Computed Property(계산 프로퍼티)
는 값을 계산한다.
저장 프로퍼티는 클래스와 구조체에서만 사용 가능한 반면,
계산 프로퍼티는 클래스, 구조체, 열거형 모두에서 사용 가능함.
저장, 계산 프로퍼티는 보통 특정 타입의 인스턴스에 쓰이는데, 타입 프로퍼티처럼 자료형에 쓰이는 경우도 있음!
게다가 Property Observer
라는 것을 정의해서 프로퍼티의 값이 바뀌는지 감시해서 원하는 동작을 할 수도 있다.
이건 내가 정의한 저장 프로퍼티에 추가할 수도 있고, superclass
를 상속받는 subclass
에도 추가할 수 있다.
또 Property Wrapper
를 사용해서 여러 프로퍼티의 getter, setter에서 코드를 재사용할 수도 있다.
특정 클래스나 구조체의 인스턴스에 저장된 변수 혹은 상수.
struct FixedLengthRange {
var firstValue: Int
let length: Int
}
var rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3)
// the range represents integer values 0, 1, and 2
rangeOfThreeItems.firstValue = 6
// the range now represents integer values 6, 7, and 8
firstValue, length가 저장 프로퍼티임.
사용되기 전까지 값이 계산되지 않는 저장 프로퍼티.
선언부분 앞에 lazy 키워드를 붙여서 나타냄.
프로퍼티의 초기값이 인스턴스의 초기화가 끝난 후 정해지는 외부 요인에 의존적일 때 lazy
를 사용한다.
복잡하거나 비싼 계산을 요하기 때문에 필요로 하기 전까지 실행되지 않는 프로퍼티의 초기값을 설정할 때도 유용하게 쓰임.
class DataImporter {
/*
외부 파일로부터 데이터를 받아오는 클래스.
초기화하는데 상당하 시간이 걸림.
*/
var filename = "data.txt"
// 데이터 임포트하는 기능 여기 구현
}
class DataManager {
lazy var importer = DataImporter()
var data: [String] = []
// 데이터 관리 기능 여기 구현
}
let manager = DataManager()
manager.data.append("Some data")
manager.data.append("Some more data")
// importer 프로퍼티의 DataImporter 인스턴스 아직 안만들어짐
DataManager
의 기능 중 하나가 DataImporter
인 것이고, 요 기능은 시간이 엄~~청 오래 걸리기 때문에 사용하기 전까지는 선언을 연기하는 것.
print(manager.importer.filename)
// DataImporter 인스턴스 생성됨
// Prints "data.txt"
이렇게 사용한다고 티를 내야만 만들어진다.
클래스, 구조체, 열거형은 저장 프로퍼티 외에도 계산 프로퍼티를 정의할 수 있음.
근데 얘네는 실제로는 값을 저장하지 않고, getter
와 setter(optional)
을 가질 수 있는데, 다른 프로퍼티와 값들을 간접적으로 설정하고 반환할 수 있다.
getter
는 다른 저장/계산 프로퍼티의 값을 얻거나 연산해서 리턴해주는 거임.
그래서 무조건 return값이 있어야 한다!
setter
는 다른 저장 프로퍼티에 값을 저장할 때 사용하는데 매개변수로 받은 값을 어떻게, 어떤 값을 저장할 것인지 알려줘야 함!
계산 프로퍼티는 어떤 값을 저장하는 게 아니기 때문에 타입 추론이 불가능하다.
그래서 선언할 때 무조건 타입을 명시해 줘야 함!!
내가 어떤 타입의 값을 받아서 다른 저장 프로퍼티에 저장할 건지, 어떤 타입의 값을 반환할 건지 알려줘야 한다는 것.
그니까 계산 프로퍼티를 사용하려면 다른 저장 프로퍼티가 일단 하나 무조건 있어야 한다!!!
class Person {
var name: String = "수입푸드"
var alias: String {
get {
return name
}
set(name) {
self.name = name
}
}
}
여기서 alias
가 계산 프로퍼티이고, 저장 프로퍼티 name
을 매개변수로 받아서 쓰는 거임.
class Person {
var name: String = "swift"
var alias: String {
get {
return self.name + " velog"
}
set(name) {
self.name = name + " subscribe"
}
}
}
요런식으로 받은 저장 프로퍼티를 변형해서(계산해서) 반환해줄 수도 있다!
이제 이걸 사용하려면 어떻게 하쥬??
let swift: Person = .init()
// get에 접근
print(swift.alias)
// Prints swift velog
// set에 접근
swift.alias = "수입푸드"
print(swift.name)
// Prints 수입푸드 subscribe
이렇게 저장 프로퍼티랑 비슷하게 쓰면 됨.
계산 프로퍼티인
alias
의 값을 읽을 때는getter
가 실행되고,
alias
의 값을 쓸 때는 "수입푸드"가 매개변수로 넘어가setter
부분이 실행되는 것!
setter의 매개변수에는 타입을 따로 지정해주지 않았다.
왜냐면 계산 프로퍼티에서 이미 타입을 적어놨는데, 무적권 그 타입의 매개변수가 들어올 것이기 때문!
근데 이 이름마저 생략할 수가 있다.
Swift에서 기본으로 정해주는 newValue
를 사용하면 됨.
class Person {
var name: String = "swift"
var alias: String {
get {
return self.name + " velog"
}
set {
self.name = newValue + " subscribe"
}
}
}
이렇게 newValue를 원하는 자리에다가 넣어주면 됨!
getter도 간단하게 만들어줄 수 있다.역시 생략에 미친 스위프트..
getter의 표현식이 하나라면 return
을 생략할 수 있음. 요런식으로.
class Person {
var name: String = "swift"
var alias: String {
get {
self.name + " velog"
}
set {
self.name = newValue + " subscribe"
}
}
}
setter가 없는 계산 프로퍼티를 Read-Only Computed Property
라고 부른다(get-only
라고도 하는듯?).
항상 어떤 값을 리턴하고, dot syntax를 통해 접근이 가능하지만 다른 값으로 set하는 것을 불가능하다.
struct Cuboid {
var width = 0.0, height = 0.0, depth = 0.0
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)")
// Prints "the volume of fourByFiveByTwo is 40.0"
이런 식으로 set가 없으므로 get도 생략하고 바로 return이 나올 수 있다.
약간 클로저랑 비슷한 것 같기도?
암튼 volume으로 원하는 값을 계산할 수가 있다!