이전 포스팅에서 프로퍼티는 값을 저장하기위해 클래스나 구조체 내에서 정의된 변수나 상수라고 말한 적이 있다. 사실 이 것은 프로퍼티 역할의 일부에 불과하다. 자세히 말하자면 프로퍼티의 역할은 값을 제공하는 것이다.
굳이 저장이 아니라 제공한다고 한 것은 프로퍼티 중 일부는 값을 저장하진 않지만 값을 제공하는 특성을 가지기 때문이다. 여기서 값 저장 여부를 기준으로 프로퍼티가 나뉘게 되는데 저장 프로퍼티(Stored Property)와 연산 프로퍼티(Computed Property)이다.
이 뿐만 아니라 구조체나 클래스를 만들 때 인스턴스를 생성하는데 이 인스턴스에 소속되는 프로퍼티를 인스턴스 프로퍼티(Instance Property), 클래스나 구조체 자체에 소속되어 값을 가지는 프로퍼티를 타입 프로퍼티(Type Property)로 나뉘게 된다.
역할에 따라 | 소속에 따라 | |
프로퍼티 분류 | 저장 프로퍼티(Stored Propety) | 인스턴스 프로퍼티(Instance Property) |
연산 프로퍼티(Computed Property | 타입 프로퍼티(Type Property) |
이번 포스팅에선 저장 프로퍼티에 대해 자세히 알아보겠다.
저장 프로퍼티
저장 프로퍼티(Stored Property)는 클래스 내에서 선언된 변수나 상수를 부르는 이름입니다. 일반 변수나 상수를 선언할 때 초기값을 할당할 수 있는 것처럼 저장 프로퍼티를 선언할 때도 초기값을 할당할 수 있다.
반드시 선언하는 시점에서 초기값을 할당해야하는 것은 아니다. 초기화 구문엣서 초기값을 설정해도 상관 없다. 구조체의 경우 멤버와이즈 구문을 통해 초기화를 하면 된다.
하지만 클래스에서 프로퍼티를 선언할 때엔 초기값을 설정해주지 않으면 주의가 필요하다. 클래스에서 프로퍼티 선언 시 초기값을 할당하지 않은 저장 프로퍼티는 반드시 옵셔널 타입으로 선언해주어야 한다. 스위프트에선 클래스의 프로퍼티에 값이 비어 있으면 인스턴스 생성 시 무조건 nil로 초기화하기 때문이다. 옵셔널 타입으로 선언하지 않고 초기화 구문에서 바로 저장프로퍼티의 값을 초기화 해줘도 된다.
반면 구조체는 초기값으로부터 자유로워 초기값을 할당하지 않고 옵셔널 타입으로 지정하지 않아도 된다. 멤버와이즈 초기화 구문이 제공되기 때문이다.
다음 구문을 보면서 이야기해보자.
class User {
var name: String
}
단순히 이렇게 저장 프로퍼티를 선언할 경우 오류가 발생한다. 바로 저장프로퍼티의 초기화가 이루어져있지 않기 때문이다. 이를 해결하기 위해선 다음 세 가지 해결책 중 하나를 선택해 적용해주어야 한다.
// 초기화 구문 작성
class User {
var name: String
init() {
self.name = ""
}
}
// 옵셔널 타입으로 선언
class User {
var name: String?
}
(또는)
class User {
var name: String!
}
// 프로퍼티에 초기값 할당
class User {
var name: String = ""
}
첫 번째 init()은 초기화 구문이다. 초기화 메소드라고 불린다. 일반적인 메소드와 다르게 직접 호출되는 것이 아니라 인스턴스가 생성될 때 간접적으로 호출되는 경우가 대부분이다. init 메소드 내부에서 작성된 구문이 인스턴스가 생성될 때 실행되는 것이다. self 키워드의 경우 클래스에서 선언된 프로퍼티나 메소드를 구분하기 위해 붙이는 것이다. 이에 대해선 추후 포스팅에서 자세히 다루도록 하겠다.
두 번째는 옵셔널 타입으로 초기화를 해주었고 세 번째는 선언과 동시에 초기화를 해주었다.
✅ 저장 프로퍼티의 분류
저장 프로퍼티는 두 가지로 나눌 수 있다.
- var 키워드로 정의되는 변수형 저장 프로퍼티(멤버 변수)
- let 키워드로 정의되는 상수형 저장 프로퍼티(멤버 상수)
프로퍼티는 변수나 상수의 성격을 그대로 가진다. var 키워드로 정의한 멤버 변수는 값을 자유롭게 수정이 가능하나 let 키워드로 정의한 멤버 상수는 최초에 할당된 값이 그대로 변경없이 유지된다. 다음 예제를 살펴보며 이해해보자.
// 고정 길이 범위 구조체
struct FixedLengthRange {
var startValue: Int // 시작값
let length: Int // 값의 범위
}
// 가변 길이 범위 구조체
struct FlexibleLengthRange {
let startValue: Int // 시작값
var length: Int // 값의 범위
}
// 아래 구조체 인스턴스는 정수값 0, 1, 2를 의미
var rangeOfFixedIntegers = FixedLengthRange(startVale: 0, length: 3)
// 시작값을 변경하면 정수값 4, 5, 6을 의미(length가 상수이므로 그대로 3 유지하기 때문)
rangeOfFixed Integers = 4
// 아래 구조체 인스턴스는 정수값 0, 1, 2를 의미
var rangeOfFlexibleIntegeres = FlexibleLengthRange(startValue: 0, length: 3)
// 아래처럼 범위값을 변경하면 정수값 0, 1, 2, 3, 4를 의미(length가 변수이므로 변화 가능)
rangeOfFlexibleIntegers = 5
이처럼 변수 부분은 수정이 가능하지만 주의해야할 점은 구조체 인스턴스를 상수에 할당할 경우이다. 인스턴스를 변수에 할당하면 얼마든지 자유롭게 값을 수정할 수 있다. 하지만 상수로 선언할 경우 이가 불가능하다.
// 변수에 할당된 구조체 인스턴스
var variableOfInstance = FixedLengthRange(startValue: 3, length: 4)
// 아래와 같이 저장프로퍼티 수정 가능
variableOfInstance.startValue = 0 // ( O )
// 상수에 할당된 구조체 인스턴스
let constantsOfInstance = FixedLengthRange(startValue: 3, length: 4)
// 아래와 같이 저장프로퍼티 수정하면 오류 발생
constantsOfInstance.startValue = 0 ( X )
반면, 클래스는 이러한 주의점이 적용되지 않으므로 클래스 인스턴스를 상수에 핟랑하더라도 클래스 내에서 변수로 선언한 저장 프로퍼티는 얼마든지 값을 수정할 수 있다. 클래스는 참조에 의한 전달 방식으로 인스턴스의 레퍼런스가 변수나 상수에 할당되기 때문이다. 따라서 클래스는 저장프로퍼티의 값이 바뀌더라도 상수에 할당된 인스턴스의 레퍼런스는 변경되지 않는다.
✅ 지연 저장 프로퍼티
일반적으로 저장 프로퍼티는 클래스 인스턴스가 처음 생성될 때 함께 초기화되지만, 저장 프로퍼티 정의 앞에 lazy키워드가 붙으면 예외이다. 이 키워드는 저장 프로퍼티의 초기화를 지연시킨다.
클래스 인스턴스가 생성되어 모든 저장 프로퍼티가 만들어 지더라도 lazy 키워드가 붙은 프로퍼티는 선언만 될 뿐 초기화되지 않고 대기하다가 프로퍼티가 호출되는 순간에 초기화된다. 만약 이 프로퍼티에 클래스나 구조체 인스턴스가 대입된다면, 프로퍼티가 호출되기 전까지는 해당 인스턴스는 초기화되지 않는다. 이처럼 호출되기 전에 선언만 된 상태로 대기하다가 실제로 호출되는 시점에서 초기화가 이루어지는 저장 프로퍼티를 지연 저장 프로퍼티 라고한다.
class onCreate {
init() {
print("OnCreate!!!")
}
}
class LazyText {
var base = 0
lazy var late = OnCreate()
init() {
print("Lazy Test")
}
}
let lz = LazyTest()
// "Lazy Test"
lz.late
// "On Create!!!"
// lazy 키워드가 붙지 않은 경우
let lz = LazyTest()
// "Lazy Test"
// "On Create!!!" >> 두 개가 한꺼번에 출력됨
위 예제를 보아 지연 저장 프로퍼티에 대입된 인스턴스는 프로퍼티가 처음 호출되는 시점에서 생성된다는 것을 알 수 있다. 지연 프로퍼티는 처음으로 호출이 발생할 때 값을 평가해 초기화되며, 이후 두 번째 호출부터는 처음 초기화된 값을 그대로 사용하게 된다. 다시 초기화 되지는 않는다.
출처 : 꼼꼼한 재은씨의 Swift 문법편
'Swift > 문법' 카테고리의 다른 글
스위프트(Swift) - Raw String (0) | 2022.07.13 |
---|---|
스위프트(Swift) - 구조체와 클래스 Ⅱ. 프로퍼티 - 연산 프로퍼티 (0) | 2022.06.02 |
스위프트(Swift) - 구조체와 클래스 Ⅰ. 기본 개념 (0) | 2022.05.31 |
스위프트(Swift) - 함수(Fuction) Ⅹ. @escaping, @autoescape (0) | 2022.05.26 |
스위프트(Swift) - 함수(Function) Ⅸ. 트레일링 클로저(Trailing Closure) (0) | 2022.05.25 |