💡저장 프로퍼티, 지연 저장 프로퍼티, 연산 프로퍼티, 타입 프로퍼티, 프로퍼티 감시자
클래스, 구조체, 열거형과 내부에 구현. 타입과 관련된 값을 저장할 수도, 연산할 수도 있다. 열거형의 내부에는 연산 프로퍼티만 구현 가능하다.
클래스 혹은 구조체의 인스턴스와 연관된 값을 저장하는 기본적인 프로퍼티
var
: 변수 저장 프로퍼티let
: 상수 저장 프로퍼티기본값 및 초깃값 지정 가능
Struct : 저장 프로퍼티가 옵셔널이 아니어도 이니셜라이저를 자동 생성
Class : 저장 프로퍼티가 옵셔널이 아니면 기본값 지정 혹은 이니셜라이저를 통한 초기화 필수
struct Person {
var name: String //변수 저장 프로퍼티
var age: Int //변수 저장 프로퍼티
}
//구조체에는 저장프로퍼티를 매개변수로 하는 이니셜라이저가 내장되어있다
let sujilee: Person = Person(name: "suji", age: 29)
class Person {
var name: String //변수 저장 프로퍼티
let age: Int //상수 저장 프로퍼티
//프로퍼티의 기본값(초기값)이 없으면 이니셜라이저를 정의해야함
init(name: String, age: Int) {
self.name = name
self.age = age
}
}
//사용자 정의 이니셜라이저를 사용해야 초기값을 할당할 수 있다
let sujilee: Person = Person(name: "suji", age: 29)
struct Coordinates {
var x = 0 //타입 추론 저장 프로퍼티
var y = 0 //타입 추론 저장 프로퍼티
}
//기본값이 할당돼있기 때문에 인스턴스 생성 시 전달인자로 초기값을 넘길 필요가 없다
let locationOfInit = Coordinates()
//물론 기존 이니셜라이저도 그대로 사용 가능하다
let locationOfTen: Coordinates = Coordinates(x: 10, y: 10)
struct Coordinates {
var x = 0 //타입 추론 저장 프로퍼티
var y = 0 //타입 추론 저장 프로퍼티
}
let locationOfInit = Coordinates()
let locationOfTen: Coordinates = Coordinates(x: 10, y: 10)
class Information {
var location: Coordinates = Coordinates() //변수 저장 프로퍼티
var name: String = "UnKnown" //상수 저장 프로퍼티
}
//초기값을 설정된 상태기 때문에 사용자 정의 이니셜라이저를 사용할 필요 없음
let infoOfSujilee: Information = Information()
infoOfSujilee.name = "sujilee"
infoOfSujilee.location = locationOfInit
print(infoOfSujilee.name, infoOfSujilee.location)
초기값 설정이 필요없는 경우도 있다. 옵셔널 타입으로 생성된 프로퍼티의 경우.
struct Coordinates {
var x: Int
var y: Int
}
class Information {
var location: Coordinates?
let name: String
init(name: String) {
self.name = name
}
}
//location은 옵셔널 타입이기 때문에 필수가 아님
//name은 상수이기 때문에 필수
let infoOfSujilee: Information = Information(name: "suji")
//위치를 알게 된다면 그 때 위치값 할당
infoOfSujilee.location = Coordinates(x: 7, y: 7)
"A lazy stored property is a property whose initial value is not calculated until the first time it is used"
지연 저장 프로퍼티를 이해하는데 좋은 예시가 있다. 바로 인스타 스토리임. 사용자가 인스타를 실행시킨 시점에서 모든 스토리를 서버로부터 가져온다면? 쓸데 없이 메모리만 차지하고 불필요한 연산이 일어날 것이다. 스토리는 사용자가 클릭했을때만 보여주면 그만이기 때문에, 앱을 실행시킨 시점이 아니라 클릭이 이루어진 순간에 해당 스토리를 서버로부터 로드하면 됨.
import Foundation
class Person {
var name: String
//lazy var에 클로저 실행의 결과를 할당한다.
lazy var monologue: String = {
//클로저 내부에서 self 키워드로 name에 접근
//클래스 내부의 클로저에서 (클래스 객체를) self로 참조하면 메모리 누수 발생의 위험이 있음
//but 뒤의 ()를 통해 그 즉시 실행 후 결과를 리턴하고 끝나버리기때문에 메모리 누수가 발생하지 않음
return "My name is \(self.name)"
}()
init(name: String) {
self.name = name
}
}
var sujilee = Person(name: "Suji Lee")
//lazy var인 monologue 처음 사용
print(sujilee.monologue)
sujilee.name = "Lea Lee"
//프로퍼티가 "Lea Lee"로 변경되어도 처음 해당 프로퍼티를 사용하는 시점 메모리에 "Suji Lee"가 올라가기 때문에 "Suji Lee"로 출력된다.
print(sujilee.monologue)
My name is Suji Lee
My name is Suji Lee
import Foundation
class Person {
var name: String
//lazy var에 클로저 자체를 할당한다. geering: () -> String 라는 Closure.
//이럴 경우 메모리 누수의 위험이 있기 때문에 [weak self]를 통해 누수를 방지.
lazy var monologue: () -> String = { [weak self] in
return "My name is \(((self?.name))!)"
}
init(name: String) {
self.name = name
}
}
var sujilee = Person(name: "Suji Lee")
print(sujilee.monologue())
sujilee.name = "Lea Lee"
//값이 아닌 클로저 자체가 메모리에 올라가 있고, self는 내부에서 계속 클래스를 참조하기 때문에 "Lea Lee"가 출력된다.
print(sujilee.monologue())
My name is Suji Lee
My name is Lea Lee
var
로만 선언 가능. 읽기 전용만 구현 가능.
struct Student {
//인스턴스 저장 프로퍼티
var name: String = ""
var `class`: String = "SWiftUI"
var koreanAge: Int = 0
//인스턴스 연산 프로퍼티
var westernAge: Int {
get {
return koreanAge - 1
}
set(inputValue) {
koreanAge = inputValue + 1
}
}
//타입 저장 프로퍼티
static var typeDescription: String = "Student"
//인스턴스 메서드
func selfIntroduceInstanceMethod() {
print("I am \(self.name) of \(self.class)")
}
//읽기 전용 인스턴스 연산 프로퍼티
//selfIntroduceInstanceMethod()를 대체할 수 있다.
var selfIntroduceInstancePropety: String {
get {
return "I am \(self.name) of \(self.class)"
}
}
//타입 메서드
static func selfIntroduceTypeMethod() {
print("I am \(self.name) of \(self.class)")
}
//읽기 전용 타입 연산 프로퍼티
//selfIntroduceTypeMethod()를 대체할 수 있다.
//읽기 전용에서는 `get` 생략 가능
staic var selfIntroduceTypeProperty: String {
return "I am \(self.name) of \(self.class)"
}
//타입 연산 프로퍼티 사용
print(Student.selfIntroTypeProperty)
var sujilee: Student = Student()
sujilee.koreanAge = 29
//인스턴스 연산 프로퍼티 사용
print(sujilee.selfIntroduceInstanceProperty)
print("my koreaAge is \(sujilee.koreanAge) while westernAge is \(sujilee.westernAge)")
}
struct Money {
var currencyRate: Double= 1100
var dollar: Double = 0
var won: Double {
get {
return dollar * currencyRate
}
set {
//암시적 파라미터 'newValue' 사용 가능
dollar = newValue / currencyRate
}
}
}
var moneyInMyPocket = Money()
moneyInMyPocket.won = 11000
print(moneyInMyPocket.won)
moneyInMyPocket.dollar = 10
print(moneyInMyPocket.won)
: 특정한 구조체, 클래스에 속하는 저장 프로퍼티, 연산 프로퍼티. 해당 타입의 인스턴스가 생성되었을 때 사용할 수 있는 프로퍼티이다.
: 각각의 인스턴스가 아닌 타입 자체에 속하는 프로퍼티. 타입 프로퍼티의 값은 오직 하나이다.
: 인스턴스를 생성하지 않고도 사용 가능.
타입 프로퍼티는 모든 타입이 사용할 수 있는 상수 프로퍼티 또는 글로벌 변수 프로퍼티처럼 특정 타입이 모든 인스턴스가 사용할 공통적인 값을 정의하는데 유용하다.
static
키워드를 사용해 정의 lazy
키워드를 사용할 필요가 없다. satic
이 아닌 class
키워드를 사용해 정의한 연산 타입 프로퍼티는 해당 클래스를 상속받은 서브클래스에서 구현부를 재정의(override) 할 수 있다.//구조체에서의 사용
struct someStructure {
//저장 타입 프로퍼티
static var storedTypeProperty: String = "type value"
//연산 타입 프로퍼티
static var computedTypeProperty: Int {
return 1206
}
}
//열거형에서의 사용
struct someEnumeration {
//저장 타입 프로퍼티
static var storedTypeProperty: String = "type value"
//연산 타입 프로퍼티
static var computedTypeProperty: Int {
return 1206
}
}
class SuperClass {
static var storedTypeProperty: String = "type value"
static var computedTypeProperty: Int {
return 1206
}
//재정의 가능한 타입 연산 프로퍼티
class var overridableComputedTypeProperty: Int {
return 1206
}
}
class SubClass: SuperClass {
//override 키워드를 사용한 연산 타입 프로퍼티 재정의
override static var overridableComputedTypeProperty: Int {
return 941206
}
}
class SomeClass {
static var storedTypeProperty: Int = 1994
var storedInstanceProperty: Int = 0 {
didSet {
//Self.storedTypeProperty == SomeClass.storedTypeProperty
Self.storedTypeProperty = storedInstanceProperty + 42
}
}
static var computedTypeProperty: Int {
get {
return storedTypeProperty
}
set {
storedTypeProperty = newValue
}
}
}
print(SomeClass.storedTypeProperty)
//OUTPUT : 1994
SomeClass.storedTypeProperty = 1206
print(SomeClass.storedTypeProperty)
//OUTPUT : 1206
print(SomeClass.computedTypeProperty)
//OUTPUT : 1206
var someInstance: SomeClass = SomeClass()
print(someInstance.storedInstanceProperty)
//OUTPUT : 0
someInstance.storedInstanceProperty = 1206
print(SomeClass.computedTypeProperty)
//OUTPUT : 1248
프로퍼티 감시자를 사용해 프로퍼티의 값이 변경될 때 원하는 동작을 수행할 수 있다. 기존 값과 동일하게 변경되어도 항상 동작함. 프로퍼티 감시자는 연산 프로퍼티에 사용할 수 없다.
newValue
사용 가능.oldValue
사용 가능.struct Money {
//프로퍼티 옵저버 사용
var currencyRate: Double = 1100 {
willSet(newRate) {
print("currency rate has changed from \(currencyRate) to \(newRate)")
}
didSet(oldRate) {
print("current rate has changed from \(oldRate) to \(currenyRate)")
}
}
//프로퍼티 옵저버 사용
var dollar: Double = 0 {
//willSet의 암시적 파라미터(newValue) 사용
willSet {
print("earlier : \(dollar), later: \(newValue)")
}
//didSet의 암시적 파라미터(oldValue) 사용
didSet {
print("earlier : \(oldValue), later: \(dollar)")
}
}
//연산 프로퍼티
var won: Double {
get {
return dollar * currencyRate
}
set {
dollar = newValue / currencyRate
}
//연산 프로퍼티와 프로퍼티 옵저버 동시 구현 불가능
//ERROR
willSet {
//code
}
}
}
var moneyInMyPocket: Money = Money()
//currency rate shift : 1100.0 to 1150.0
moneyInMyPocket.currencyRate = 1150
//dollar will shift : 0.0 to 10.0
moneyInMyPocket.dollar = 10
print(moneyInMyPocket.won)
//OUTPUT: 11500.0
함수, 메서드, 클로저, 타입 등의 외부에 위치한 지역변수, 전역 변수도 저장 프로퍼티와 연산 프로퍼티의 기능을 사용할 수 있다.
var a: Int = 100
var b: Int = 200
var sum: Int {
return a + b
}
print(sum)
struct Student {
//인스턴스 저장 프로퍼티
var name: String = ""
var koreanAge: Int = 0
var birthMonth: Int = 12
//인스턴스 연산 프로퍼티
var westernAge: Int {
get {
return koreanAge - 1
}
set(inputValue) {
if self.birthMonth > 6 {
koreanAge = inputValue + 1
} else {
koreanAge = inputValue + 2
}
}
}
}
var sujilee: Student = Student()
sujilee.koreanAge = 29
//연산 프로퍼티 westernAge를 getter로 읽었을 때
print(sujilee.westernAge)
//연산 프로퍼티 westernAge를 setter로 변경했을 때
sujilee.westernAge = 27
print(sujilee.koreanAge)
var jji: Student = Student()
jji.koreanAge = 29
jji.birthMonth = 3
jji.westernAge = 27
print(jji.koreanAge)
참고 :
https://yagom.github.io/swift_basic/contents/13_property/]
https://seons-dev.tistory.com/entry/Swift-%EA%B8%B0%EC%B4%88-%EB%AC%B8%EB%B2%95-%EB%AA%A8%EC%9D%8C#%ED%94%84%EB%A1%9C%ED%8D%BC%ED%8B%B0