티스토리 뷰

Swift

[Swift] Property

learner._.Kio 2021. 3. 11. 00:39

목차

  • Stored Property (저장 속성)
    • Variable Stored Property (변수 저장 속성)
    • Constant Stored Property (상수 저장 속성)
    • Lazy Stored Properties (지연 저장 속성)
  • Computed Property (계산된 속성)
  • Property Observer (속성 감시자)
  • Type Property
  • self & super


Stored Property (저장 속성)

저장 속성은 Class와 Struct에서 선언할 수 있다. 저장 속성은 인스턴스에 속한 속성이다. 인스턴스가 생성될 때마다 새로운 저장공간이 생성된다.

Variable Stored Property (변수 저장 속성)

var name: Type = DefalutValue

Constant Stored Property (상수 저장 속성)

let name: Type = DefaultValue
class Person {
    let name: String = "John Doe" // Variable Stored Property (변수 저장 속성)
    var age: Int = 33 // Constant Stored Property (상수 저장 속성)
}

Explicit Member Experssion (명시적 멤버 표현식)

Dot Syntax(점 문법)이라고 불리지만, Swift에서는 Explicit Member Experssion(명시적 멤버 표현식)이라고 한다.

선언

instanceName.propertyName
instanceName.propertyName = NewValue

Class Property

class Person {
    let name: String = "Hailey" 
    var age: Int = 17
}

let p = Person()
p.name
p.age

p.age = 35 // Variable Stored Property이기 때문에 변경 가능
p.name = "Kio" // error

Struct Property

struct Person {
    let name: String = "Hailey" 
    var age: Int = 17
}

let p = Person()
p.name
p.age

p.age = 35 // error, 'p' is a 'let' constant
  • 구조체를 상수(let)로 선언하면 구조체에 있는 모든 속성이 상수가 된다.

  • 구조체의 가변성은 속성에 가변성에 영향을 준다.

  • 저장 속성을 바꿔야 한다며 변수(var)에 저장해야 한다.

Lazy Stored Properties (지연 저장 속성)

지연 저장 속성은 초기화를 지연시킨다.

선언

  • 저장 속성 앞에 lazy를 선언하면 된다.
  • 지연 저장 속성은 인스턴스가 초기화 된 이후에 개별적으로 저장된다.
    • lazy var로 선언
    • lazy let 선언불가
    • 선언시점에 기본값 저장해야 한다.
lazy var name: Type = DefaultValue
  • lazy let은 사용할 수 없다.

    struct Image {
        init() {
            print("new image")
        }
    }
    
    struct BlogPost {
        let title: String = "Title"
        let content: String = "Content"
        let attachment: Image = Image() 
    }
    
    let post = BlogPost() // new image
    struct BlogPost {
        let title: String = "Title"
        let content: String = "Content"
        lazy let attachment: Image = Image() // error, 'lazy' cannot be used on a let, Replace 'lazy' with"
    }
    
    let post = BlogPost() // new image
  • lazy var로 바꾸는지 출력이 되지 않는다.

    struct Image {
        init() {
            print("new image")
        }
    }
    
    struct BlogPost {
        let title: String = "Title"
        let content: String = "Content"
        lazy var attachment: Image = Image() 
    }
    
    let post = BlogPost()
    post.attachment // error, Cannot use mutation getter on immutable value: 'pose' is 'let' constant
    • 구조체에서 지연 저장 속성을 사용한다면 let postvar post로 바꿔야 한다.
    var post = BlogPost()
    post.attachment // new image
    • 시간 속성도 넣어보자
      • 근데 이건 뭔소린지 모르겠다아....
    struct Image {
        init() {
            print("new image")
        }
    }
    
    struct BlogPost {
        let title: String = "Title"
        let content: String = "Content"
        lazy var attachment: Image = Image()
    
        let date: Date = Date()
    
        lazy var formattedDate: String = {
           let f = DateFormatter()
            f.dateStyle = .long
            f.timeStyle = .medium
            return f.string(from: date)
        }()
    }
    
    var post = BlogPost()
    post.attachment // new image
    
    post.formattedDate
    // 실행할 때마다 초기화된다!
  • 구조체의 가변성과 속성의 관계?

  • 지연 저장 속성?

  • 속성 초기화 패턴?



Computed Property (계산된 속성)

계산 속성이라고 부른다. 다른 속성을 기반으로 속성값이 결정된다. 저장 속성은 값을 저장할 메모리 공간을 가지고 있지만, 계산 속성은 메모리 공간을 가지지 않는다. 다른 속성에 저장된 값을 읽어, 필요한 계산을 실행하여 리턴하거나 속성으로 전달되는 값을 다른 값에 전달한다. 이러한 특징 때문에 속성에 접근할 때마다 다른 값이 전달 될 수 있다.

선언

var name: Type {
    get {
        statements
        return expr
    }
    set(name) {
        statements
    }
}
  • 항상 var로 저장해야 한다. 계산 속성은 let으로 선언할 수 없다.

  • class, struct, enum에 추가할 수 있다.

  • 형식추론을 사용할 수 없고, 자료형을 명시해야 한다.

  • get block / getter - get이 포함된 블럭

  • set block / setter - set이 포함된 블럭

    • 값을 저장할 때 실행

Set 블럭

class Person {
   var name: String
   var yearOfBirth: Int

   init(name: String, year: Int) {
      self.name = name
      self.yearOfBirth = year
   }

    var age: Int {
        get {
            let calendar = Calendar.current
            let now = Date()
            let year = calendar.component(.year, from: now)
            return year - yearOfBirth
        }
        set {
            let calenrdar = Calendar.current
            let now = Date()
            let year = calenrdar.component(.year, from: now)
            yearOfBirth = year - newValue
        }
    }
}

let p = Person(name: "Kio", year: 2002) // Person
p.age // 19

p.age = 50 // Person
p.yearOfBirth // 1971 (2021년 기준)

get 블록 (읽기전용 계산속성)

// #1
var name: Type {
    get {
        statements
        return expr
    }
}
// #2
var name: Type {
    statements
    return expr
}
  • #1 == #2 는 동일한 표현이다.

#1

class Person {
   var name: String
   var yearOfBirth: Int

   init(name: String, year: Int) {
      self.name = name
      self.yearOfBirth = year
   }

    var age: Int {
        get {
            let calendar = Calendar.current
            let now = Date()
            let year = calendar.component(.year, from: now)
            return year - yearOfBirth
        }
    }
}

let p = Person(name: "Kio", year: 2002)
p.age // 19

p.yearOfBirth // 2002

#2

class Person {
   var name: String
   var yearOfBirth: Int

   init(name: String, year: Int) {
      self.name = name
      self.yearOfBirth = year
   }

    var age: Int {
        let calendar = Calendar.current
        let now = Date()
        let year = calendar.component(.year, from: now)
        return year - yearOfBirth
    }
}

let p = Person(name: "Kio", year: 2002)
p.age // 19

p.yearOfBirth // 2002
  • Int 다음에 할당연산자가 없다. 그렇다면 읽기전용 계산 속성이다.
  • 쓰기전용 계산속성은 없다.


Property Observer (속성 감시자)

선언

var name: Type = DefaultValue {
    willSet(name) {
        statement
    }
    didSet(name) {
        statement
    }
}
  • willSet

    • 속성의 값이 변환되기 전에 호출
    • parameter를 생략하면 newValue 값이 형성
  • didSet

    • 값이 저장된 직후에 호출
    • parameter를 생략하면 oldValue 값이 형성

예시

class Size {
    var width = 0.0 {
        willSet {
            print("새로운 값은 \(newValue)")
        }
        didSet {
            print("이전 값은 \(oldValue)")
        }
    }
}

let s = Size()
s.width = 123
// 새로운 값은 123.0
// 이전 값은 0.0
  • willSet or didSet 중 하나는 사용하여야 Property Observer (속성 감시자)가 된다.


Type Property (형식 속성)

  • 형식 속성은 Class, Structure,Enumeration에 모두 추가할 수 있다.
  • Stored Property(저장 속성)Type Property(형식 속성)으로 선언 → 저장 형식 속성
  • Computed Property(계산된 속성)Type Property(형식 속성)으로 선언 → 계산 형식 속성

저장 형식 속성 선언

// Variable Stored Type Property
static var name: Type = DefaultValue
// Constant Stored Type Property
static let name: Type = DefaultValue

TypeName.propertyName
  • 항상 원하는 값으로 자료형을 설정해줘야 한다.

예시

class Math {
    static let pi = 3.14
}

let m = Math()
m.pi // error
Math.pi // 3.14

계산 형식 속성 선언

// static
// static으로 선언하면 Overrideing이 금지된다.
static var name: Type {
    get {
        statements
        return expr
    }
    set(name) {
        statement
    }
}

// class
// class에서 제한적으로 사용된다. 
// Overrideing은 허용된다.
class var name: Type {
    get {
        statements
        return expr
    }
    set(name) {
        statements
    }
}

예시

enum Weekday: Int {
    case sunday = 1, monday, tuesday, wednesday, thursday, friday, saturday

    static var today: Weekday {
        let cal = Calendar.current
        let today = Date()
        let weekday = cal.component(.weekday, from: today)
        return Weekday(rawValue: weekday)!
    }
}
\
Weekday.today // wednesday, 오늘이 수요일이었음!

흐음.... 머지 이게...



self & super

Self 표현식

// 인스턴스 자체에 접근할 때
self

// 인스턴스 속성에 접근할 때  
self.propertyName

// 인스턴스 메서드를 호출할 때
self.method()

// 서브스크립트를 호출할 때
self[index] 

// 동일한 형식의 다른 생성자를 호출할 때
self.init(parameters)

예시

class Size {
   var width = 0.0
   var height = 0.0

    func multiply() -> Double {
        // return self.width * self.height
        return width * height // self 생략가능
    }

    var area: Double {
        // return self.clacArea()
        return multiply() // self 생략가능
    }

    func update(width: Double, height: Double) {
        self.width = width
        self.height = height
        // self로 파라미터와 구별해줌
    }

    func doSomething() {
        let c = { self.width * self.height }
        // Clousere 내부에서는 self를 캡처해야한다.
    }

    static let unit = ""

    // 형식 메서드
    static func doSomething() {
        self.width // error, 형식 메서드에서 인스턴스 메서드로 접근하는 것은 불가능
        self.unit // 접근가능
        unit // 접근가능
    }
}
  • self는 현재 인스턴스에 접근하기 위해 사용하는 특별한 속성이다.
  • self를 타입 멤버에서 사용하면 인스턴스가 아닌 형식 자체를 나타낸다.
struct Size {
   var width = 0.0
   var height = 0.0

    mutating func reset(value: Double) {
       // width = value // 문제없음
       // height = value // 문제없음
        self = Size(width: value, height: value)
    }
}
  • 이 패턴은 생성자를 통해 초기화 할 수 있다는 장점이 있지만, Class에서는 사용할 수 없다.

super 표현식

super.propertyName
super.method()
super[index]
super.init(parameters)
  • self 속성은 Structure, Class, Enumeration에서 사용한다
  • super 속성은 Class에서만 사용한다.

'Swift' 카테고리의 다른 글

[Swift] Generic  (0) 2021.03.23
[Swift] Error Handling  (0) 2021.03.16
[Swift] Collection - Array, Dictionary, Set  (0) 2021.03.09
[Swift] Structures and Classes  (0) 2021.03.08
[Swift] Closure  (0) 2021.03.08
댓글