Danny의 iOS 컨닝페이퍼
article thumbnail
반응형

1. 속성(Properties)의 종류

크게 5가지로 나눌 수 있습니다.

  • 저장 속성 - Stored Properties
  • 지연 저장 속성- Lazy Stored Properties
  • 계산 속성 - Computed Properties
  • 타입 속성 - Type Properties
  • 속성 감시자 - Property Observer

 

 

2. 저장 속성(Stored Properties)

값이 저장되는 일반적인 속성. 이 자체가 메모리 공간을 갖습니다.

변수(var)상수(let)로 선언 가능합니다.

 

선언

<swift>
class Person { let name: String = "Daniel" var age: Int = 30 } let daniel = Person() daniel.name // "Daniel" daniel.age // 30

 

 

3. 지연 저장속성(Lazy Stored Properties)

처음부터 초기화가 필요하지 않은 경우에 지연 저장속성을 통하여 초기화를 지연시킵니다.

 

특징

  • 오직 변수(var)로만 선언 가능합니다
  • 해당 변수에 접근하는 시점에 초기화됩니다.(메모리 공간이 생기고 저장됩니다)
  • 항상 default값(초기값, 기본값)반드시 필요합니다. 표현식도 가능합니다. (ex. 함수, 클로저)

 

지연 저장 속성을 사용하는 이유

  1. 이미지와 같이 일반적으로 메모리공간을 많이 차지하는 곳에서 사용합니다.
  2. 다른 프로퍼티를 의존(이용) 해야 할 때 사용합니다.

 

선언

<swift>
lazy var lazyStoredProperties: SomeType = SomeType()
<swift>
struct IPhone { var name: String init(name: String) { self.name = name print("\(name)는 IPhone을 갖고 있습니다") } } class Person { let name: String = "Daniel" var age: Int = 30 lazy var phone = IPhone(name: name) } let daniel = Person() daniel.name // "Daniel" daniel.age // 30 daniel.phone // Daniel는 IPhone을 갖고 있습니다

이 코드에서 지연 저장속성의 특징을 전부 알 수 있습니다.

 

1. 지연속성을 통해 같은 클래스 내부의 다른 프로퍼티를 이용하기.

보통 Person의 객체생성하게 되면 컴파일 시 각각의 속성들은 동시에 메모리에 올라가기 때문에 내부 속성인name, age, phone은 각각 다른 속성에 접근하지 못합니다.

하지만 지연 특성을 사용하면 다른 속성에 접근이 가능해지죠. 왜냐하면 호출이 되지 않는 한 메모리가 생성되지 않기 때문입니다.

(예제 - 속성 phone에서 name으로 접근이 가능!)

 

2. 지연속성은 그 속성에 접근하지 않는 이상 메모리가 생성되지 않는다.

  • class Person에서 lazy var로 IPhone 객체생성 (메모리 생성 x)
  • danielPerson의 객체 생성 (메모리 생성 x)
  • daniel.phone을 호출 (메모리 생성 O)

즉, 지연 속성인 daniel.phone으로 접근하면 그제야 메모리가 생성되고 저장이 되죠!

 

 

아래 그림을 보면서 한 번 더 이해해 봅시다.

이와 같이 초기화가 되더라도 메모리가 없다가 그 속성으로 접근하는 순간 메모리가 생성됩니다.

 

 

4. 계산속성(Computed Properties)

계산속성은 실질적으로는 메서드(함수)입니다.

 

특징

  • gettersetter가 있습니다.
    • getter는 무조건 구현을 해줘야 합니다.
    • setter는 생략이 가능합니다.
  • 메모리 구조가 메서드와 동일합니다 (메모리 공간을 갖지 않고 속성에 접근할 때 계산을 한 뒤, 결과를 나타냅니다)
  • 자료형 선언을 해야 합니다.(형식추론 안 돼요) 왜냐하면 메서드이기 때문에 input, return이 필요한 개념이죠.
  • 입력(input)출력(return)을 동시에 사용하는 함수를 만들려면 2개의 함수를 따로 만들어 줘야 합니다.
    하지만 계산속성은 두 가지 메서드를 한 개만으로 구현이 가능합니다 (깔끔해지죠)
  • 외부에서 보기에 속성이름으로 설정가능하므로 보다 명확해 보입니다.

 

선언

<kotlin>
var computedProperties: Type { get { // getter return SomeType } set { // setter } }

 

예제

 

예제들을 보면서 이해해 봅시다.

 

Q : 현재 몇 살인가요?

Q : 나이를 입력해 태어난 날짜 구해보기

<swift>
class Person { var birth: Int = 1991 var age: Int { get { // getter는 반드시 구현 해야함 return 2023 - birth } set(age) { // setter는 생략 가능 self.birth = 2023 - age // set의 기본 파라미터는 set(newValue)이다. 파라미터 생략 가능 //self.birth = 2023 - newValue } } } var daniel: Person = Person() // get daniel.age // 31 // set daniel.age = 50 daniel.birth // 1973

 

다음 예제는 집에 벽에 페인트를 칠해 봅시다.

 

Q : 페인트 한통당 1.5m^2의 면적을 칠할 수 있습니다.

Q : 집의 면적을 칠하려면 몇 개의 페인트 통이 필요할까요? 

Q : 5개의 페인트 통으로는 얼마만큼의 면적을 칠할 수 있을까요?

<kotlin>
var width: Float = 1.8 var height: Float = 2.6 // 집의 면적을 칠하려면 몇개의 페인트 통이 필요할까? (get) // 5개의 페인트 통으로는 얼마만큼의 면적을 칠할 수 있을까? (set) var bucketsOfPaints: Int { get { let areaCoveredPerBuckets: Float = 1.5 let area = width * height // 면적 4.68 let numberOfBuckets = area / areaCoveredPerBuckets // 3.12개의 페인트 통 // 페인트 통을 쪼갤 순 없으니 올림을 해준다. let roundUpBuckets = ceilf(numberOfBuckets) return Int(roundUpBuckets) } set { let areaCoveredPerBuckets: Float = 1.5 let areaCanCover = areaCoveredPerBuckets * Float(newValue) print("\(newValue)통을 갖고 칠할 수 있는 면적 : \(areaCanCover)") } } print(bucketsOfPaints) bucketsOfPaints = 5 // 4 // 4통을 갖고 칠할 수 있는 면적 : 7.5

 

 

5. 타입속성(Type Properties)

타입속성을 두 가지로 분류할 수 있다.

  • 저장 타입속성(Stored Type Properties
    • 상속 시 재정의(override)가 불가합니다.
    • 항상 기본값이 필요합니다.
  • 계산 타입속성(Computed Type Properties)
    • 상속 시 재정의(override) 가능한 속성이 됩니다. 계산 타입속성에서는 static대신 class키워드를 사용해 줍니다.

 

특징

  • static 키워드 사용합니다.
  • 데이터 영역에 저장되어 모든 인스턴스(복제품)들과 공유가 가능한 프로퍼티입니다.
  • 타입속성으로 접근 시 타입자체로 접근을 해야 합니다 (Type. 타입속성)
  • 보통 모든 타입이 공통적인 값을 정의하는 데 유용하게 쓰입니다
  • 저장 타입속성은 기본적으로 지연 속성을 갖고 있습니다 (속성에 처음 접근하는 순간에 초기화시킵니다)
    (참고: 여러 스레드에서 동시에 액세스 하는 경우에도 한 번만 초기화되도록 보장됩니다. Thread-Safe)

 

선언

<swift>
class Person { static let name: String = "Daniel" // 저장 타입 프로퍼티 static var fullName: String { // 계산 타입 프로퍼티 return name + " Yang" } } // 사용시 Type 자체로 접근 후 사용 Person.name // Daniel Person.fullName // Daniel Yang

 

예제

 

1. 간단하게 새로운 객체 생성 시 카운트를 해보자

<kotlin>
class Person { var name: String static var count: Int = 0 // 타입 자체에 count라는 프로퍼티가 속해 있다. // (Person.count 이런식으로 불러올 때 메모리에 생성) init(name: String) { self.name = name // 인스턴스 생성시 카운트 +1 (타입속성 이용) Person.count += 1 // 인스턴스 생성시 타입속성에 접근하기 위해서 타입에 타입속성으로 접근 Person.count } } // 인스턴스 생성시 카운트가 늘어난다. var daniel = Person(name: "Daniel") Person.count // 1 var catherin = Person(name: "Catherin") Person.count // 2 var donald = Person(name: "Donald") Person.count // 3

 

2. 계산 타입속성에서 오버라이딩(override)을 해보자

 

Person을 상속하여 Sophia를 만들어 봅시다.

 

일단 실패 예제부터 보겠습니다.

 

실패 이유

  1. 저장 타입속성은 오버라이딩이 불가능합니다
  2. 재정의 시 static 키워드 대신에 class라는 키워드를 넣어 줘야 합니다.
<kotlin>
class Person { // 저장 타입속성 // ⭐️ 저장 타입속성은 class키워드를 지원하지 않는다. class var name: String = "Daniel" // 계산 타입속성 // ⭐️ 계산 타입속성에서 오버라이드를 하려면 static대신 class키워드를 사용해야 한다. static var species: String { return "\(Person.name)은 남자입니다" } } class Sophia: Person { // Error : Cannot override mutable property with read-only property 'name' override class var name: String { return "Sophia" } // Error : Cannot override static property override class var species: String { return "\(Sophia.name)은 여자입니다" } }

 

아래 코드는 에러 없이 잘되는 걸 확인했습니다.

이와 같이 계산 타입에서만 재정의가 가능합니다

<swift>
class Person { // 계산 속성 var name: String { return "Daniel" } // 계산 타입속성 class var species: String { return "\(person.name)은 남자입니다" } } class Sophia: Person { override var name: String { return "Sophia" } // Error : Cannot override static property override class var species: String { return "\(sophia.name)은 여자입니다" } } let person = Person() let sophia = Sophia() Person.species // Daniel은 남자입니다 Sophia.species // Sophia은 여자입니다

 

 

6. 속성 감시자(Property Observer)

속성 감시자의 내용은 이글 에서 확인하세요

 

 

부족한 설명이지만, 조금은 이해 가셨나요?

틀린 내용이 있다면 언제든지 지적해 주시면 감사히 받겠습니다. 🫠
읽어주셔서 감사합니다 😃

 

반응형
profile

Danny의 iOS 컨닝페이퍼

@Danny's iOS

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!