๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
IOS๐ŸŽ/iOS+Swift

[Swift] Property - stored, computed, type / Observer

by Jouureee 2021. 4. 13.

swift ์–ธ์–ด์˜ ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ ๋ฌธ๋ฒ•์ธ property์— ๋Œ€ํ•ด ์ •๋ฆฌํ•ด๋ณด๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

์Šค์œ„ํ”„ํŠธ์˜ property๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด 3๊ฐ€์ง€๋กœ ๊ตฌ๋ถ„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

 

  1. stored Property (์ €์žฅ ํ”„๋กœํผํ‹ฐ)
  2. computed property (์—ฐ์‚ฐ ํ”„๋กœํผํ‹ฐ)
  3. type property (ํƒ€์ž… ํ”„๋กœํผํ‹ฐ)

 

๊ธฐ๋ณธ ๋ฌธ์„œ๋ฅผ ์ฐธ๊ณ ํ•˜์—ฌ ์ •๋ฆฌํ•˜์˜€์Šต๋‹ˆ๋‹ค.

https://docs.swift.org/swift-book/LanguageGuide/Properties.html

 

Properties — The Swift Programming Language (Swift 5.7)

Properties Properties associate values with a particular class, structure, or enumeration. Stored properties store constant and variable values as part of an instance, whereas computed properties calculate (rather than store) a value. Computed properties a

docs.swift.org


stored Property 

์ƒ์ˆ˜(constant)์™€ ๋ณ€์ˆ˜(variable)๊ฐ’์„ ์ธ์Šคํ„ด์Šค์˜ ์ผ๋ถ€๋กœ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.

ํด๋ž˜์Šค์™€ ๊ตฌ์กฐ์ฒด์—์„œ๋งŒ ์‚ฌ์šฉ

 

computed Property

์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•œ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜์—ฌ ์ธ์Šคํ„ด์Šค์˜ ์ผ๋ถ€๋กœ ์ €์žฅ๋ฉ๋‹ˆ๋‹ค.

ํด๋ž˜์Šค, ๊ตฌ์กฐ์ฒด, ์—ด๊ฑฐํ˜•์—์„œ ์‚ฌ์šฉ

 

type property

property ์ž์ฒด๋ฅผ ํƒ€์ž…๊ณผ ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค..? ๋ฌด์Šจ๋ง์ผ๊นŒ๋‚˜์š” ์•„๋ž˜์—์„œ ์ข€๋” ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. 

 

property observers

์‚ฌ์šฉ์ž์˜ ์•ก์…˜์— ์‘๋‹ตํ•˜๊ธฐ ์œ„ํ•ด, property ๊ฐ’์˜ ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ๊ฐ์‹œ(observing) ํ•ฉ๋‹ˆ๋‹ค. 

 


 

์ž์•„ ํ•˜๋‚˜ ํ•˜๋‚˜ ์ž์„ธํžˆ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

stored Property 

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

์šฐ๋ฆฌ๊ฐ€ ํ”ํžˆ ๋ณด๋Š” var, let ์œผ๋กœ ์„ ์–ธ๋œ firstValue, length๊ฐ€ ๋ฐ”๋กœ stored property์ธ๋ฐ์š”.FixedLengthRange ๊ฐ์ฒด๋ฅผ ํ•˜๋‚˜ ์ƒ์„ฑํ•œ ๋’ค firstValue์— ๊ฐ’์„ ๋„ฃ์–ด์คฌ์ฃ  ? ํ›„์— ์ด ๊ฐ’์— ์ ‘๊ทผํ•˜๊ฒŒ ๋œ๋‹ค๋ฉด ์ €์žฅ๋œ 6์„ ์ถœ๋ ฅํ•ด ์ค„๊ฒƒ์ž…๋‹ˆ๋‹ค. 

 

๋˜ํ•œ rangeOfThreeItems๊ฐ€ var ์ฆ‰ ๋ณ€์ˆ˜๋กœ ์„ ์–ธ๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— fistValue์— ๊ฐ’์„ ์žฌํ• ๋‹นํ•ด์ค„ ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

 

์ด๋ฅผ let์œผ๋กœ ์„ ์–ธํ•œ๋‹ค๋ฉด?

let rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3)

// this will report an error, even though firstValue is a variable property
rangeOfFourItems.firstValue = 6

firstValue๊ฐ€ ๋ถ„๋ช… var๋กœ ์„ ์–ธ๋˜์–ด ์žˆ์Œ์—๋„ error๊ฐ€ ๋‚˜๊ฒŒ ๋˜๋Š”๋ฐ์š”. ์ด๊ฒƒ์€ struct๊ฐ€ value type ์ด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

 

๊ทธ๋ ‡๋‹ค๋ฉด reference type์ธ class๋กœ ๋ฐ”๊พผ๋‹ค๋ฉด ์–ด๋–ป๊ฒŒ ๋ ๊นŒ์š”?

class FixedLengthRange {
    var firstValue: Int
    let length: Int
    init(firstValue: Int, length: Int) {
    	self.firstValue = firstValue
        self.length = length
    }
}

let rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3)

rangeOfThreeItems.firstValue = 6

๊ฐ์ฒด๊ฐ€ let์œผ๋กœ ์„ ์–ธ๋˜์—ˆ์Œ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  firstValue๋Š” class์— ์„ ์–ธ๋œ firstValue ๊ฐ’์„ ์ฐธ์กฐํ•˜๊ธฐ ๋•Œ๋ฌธ์— firstValue์— ์ ‘๊ทผ์ด ๊ฐ€๋Šฅํ•ด์ง‘๋‹ˆ๋‹ค.

 

 

Lazy Stored Properties

๊ฐ’์ด ์‚ฌ์šฉ๋˜๊ธฐ ์ „๊นŒ์ง€ ๊ณ„์‚ฐ๋˜์–ด์ง€์ง€ ์•Š๋Š” property์ž…๋‹ˆ๋‹ค. lazy๋ฅผ ์•ž์— ๋ถ™์—ฌ์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

* ์ฃผ์˜ : lazy ํ”„๋กœํผํ‹ฐ๋Š” var๋กœ ์„ ์–ธํ•ด์•ผ ํ•จ ์™œ?

๊ฐ์ฒด ์ดˆ๊ธฐํ™”๊ฐ€ ์™„๋ฃŒ๋˜๊ธฐ ์ „๊นŒ์ง€ ์ดˆ๊ธฐ ๊ฐ’์ด ์„ค์ •๋˜์ง€ ์•Š์„ ์ˆ˜๋„ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด์ฃ . 

 

lazy property๋Š” property์˜ ์ดˆ๊ธฐ ๊ฐ’์ด ์ธ์Šคํ„ด์Šค ์ดˆ๊ธฐํ™”๊ฐ€ ์™„๋ฃŒ ๋  ๋•Œ๊นŒ์ง€ ๊ฐ’์„ ์•Œ ์ˆ˜์—†๋Š” ์™ธ๋ถ€ ์š”์ธ์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์ง€๋Š” ๊ฒฝ์šฐ์— ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ property์˜ ์ดˆ๊ธฐ ๊ฐ’์— ๋ณต์žกํ•˜๊ฑฐ๋‚˜ ๊ณ„์‚ฐ ๋น„์šฉ์ด ๋งŽ์ด ๋“œ๋Š” ์„ค์ •์ด ํ•„์š”ํ•  ๋•Œ or ์ˆ˜ํ–‰ํ•˜๋ฉด ์•ˆ๋˜๋Š” ๊ฒฝ์šฐ์—๋„ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.

class DataImporter {
    /*
    DataImporter is a class to import data from an external file.
    The class is assumed to take a nontrivial amount of time to initialize.
    */
    var filename = "data.txt"
    // the DataImporter class would provide data importing functionality here
}

class DataManager {
    lazy var importer = DataImporter()
    var data = [String]()
    // the DataManager class would provide data management functionality here
}

let manager = DataManager()
manager.data.append("Some data")
manager.data.append("Some more data")
// the DataImporter instance for the importer property hasn't yet been created

์Œ .. ๋ฌด์Šจ ๋ง์ธ์ง€ ๋ณต์žกํ•˜๋‹ค.

 

DataManger์˜ importer๋ž‘ data๋ผ๋Š” property๋ฅผ ๋น„๊ตํ•ด ์‚ดํŽด๋ณด์ž

data property๋Š” manager ๊ฐ์ฒด ์ƒ์„ฑ๊ณผ ํ•จ๊ป˜ ์ƒ์„ฑ๋˜์–ด ์™ธ๋ถ€์—์„œ ์‚ฌ์šฉ๋˜์—ˆ๋‹ค. ํ•˜์ง€๋งŒ importer ๋Š” ์‚ฌ์šฉ๋˜์ง€ ์•Š์•˜์œผ๋‹ˆ ๊ฐ์ฒด ์ƒ์„ฑ์ด ๋˜์—ˆ๋”๋ผ๊ณ  ์‚ฌ์šฉ๋˜๊ธฐ ์ „๊นŒ์ง€(append์ฒ˜๋Ÿผ) ์ƒ์„ฑ๋˜์ง€ ์•Š๋Š”๋‹ค.

๊ทธ๋Ÿฌ๋ฉด ์–ธ์ œ ์ƒ์„ฑ์ด ๋˜๋Š๋ƒ .. dataImporter ํด๋ž˜์Šค์˜ property์ธ filename์ด ์‚ฌ์šฉ๋˜์–ด์•ผ importer์ด๋ผ๋Š” lazy store property๊ฐ€ ์ƒ์„ฑ๋œ๋‹ค. ๊ทธ๋Ÿฌ๋‹ˆ dataImporterํด๋ž˜์Šค์— ์˜์กด์ ์ด๊ณ  ๊ณ„์‚ฐ ๋น„์šฉ์„ ์ฒ˜์Œ๋ถ€ํ„ฐ ์ˆ˜ํ–‰ํ•˜๋Š”๊ฒƒ์ด ์•„๋‹Œ ํ•„์š”ํ• ๋•Œ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ฒƒ์ด๋ฏ€๋กœ ์ค„์ผ ์ˆ˜ ์žˆ๋‹ค.

 

 

calculated Property

์‹ค์ œ ๊ฐ’์„ ์ €์žฅํ•˜๊ณ  ์žˆ๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ getter์™€ setter๋ฅผ ์ œ๊ณตํ•ด ๊ฐ’์„ ํƒ์ƒ‰ํ•˜๊ณ  ๊ฐ„์ ‘์ ์œผ๋กœ ๋‹ค๋ฅธ ํ”„๋กœํผํ‹ฐ ๊ฐ’์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์„ ์ œ๊ณตํ•œ๋‹ค.

struct Point {
    var x = 0.0, y = 0.0
}
struct Size {
    var width = 0.0, height = 0.0
}
struct Rect {
    var origin = Point()
    var size = Size()
    var center: Point {
        get {
            let centerX = origin.x + (size.width / 2)
            let centerY = origin.y + (size.height / 2)
            return Point(x: centerX, y: centerY)
        }
        set(newCenter) {
            origin.x = newCenter.x - (size.width / 2)
            origin.y = newCenter.y - (size.height / 2)
        }
    }
}
var square = Rect(origin: Point(x: 0.0, y: 0.0),
                  size: Size(width: 10.0, height: 10.0))
let initialSquareCenter = square.center
square.center = Point(x: 15.0, y: 15.0)
print("square.origin is now at (\(square.origin.x), \(square.origin.y))")
// "square.origin is now at (10.0, 10.0)" ์ถœ๋ ฅ

์—ฌ๊ธฐ์„œ calculated property๋Š” center์ด๋‹ค. 

์ด ํ”„๋กœํผํ‹ฐ๋Š” ๊ณ„์‚ฐ๋œ ํ”„๋กœํผํ‹ฐ์˜ ์ •์˜๋Œ€๋กœ ๊ฐ’์„ ์ง์ ‘ ๊ฐ–๊ณ  ์žˆ๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ๋‹ค๋ฅธ ์ขŒํ‘œ์™€ ํฌ๊ธฐ ํ”„๋กœํผํ‹ฐ๋“ค์„ ์ ์ ˆํžˆ ์—ฐ์‚ฐํ•ด์„œ ๊ตฌํ•  ์ˆ˜ ์žˆ๋‹ค.

 

Setter์˜ ์ธ์ž ์ด๋ฆ„์„ ์•„๋ž˜์™€ ๊ฐ™์ด set(newCenter)๋ผ๊ณ  ๋ช…์‹œํ–ˆ์ง€๋งŒ ๋งŒ์•ฝ ์ด๋ ‡๊ฒŒ (newCenter)๋ผ๊ณ  ์ธ์ž ์ด๋ฆ„์„ ์ง€์ •ํ•˜์ง€ ์•Š์œผ๋ฉด ์ธ์ž ๊ธฐ๋ณธ ์ด๋ฆ„์ธ newValue์œผ๋กœ ์‚ฌ์šฉํ•œ๋‹ค.

 

Read-Only Computed Properties

getter๋งŒ ์žˆ๊ณ  setter๋Š” ์—†๋Š” ํ”„๋กœํผํ‹ฐ๋กœ ์ฆ‰ ์ฝ์„ ์ˆ˜๋งŒ ์žˆ์ง€ ๊ฐ’์„ ๋ณ€ํ™˜ํ•˜๊ณ  ๋‹ค๋ฅธ ๊ฐ’์œผ๋กœ ์žฌํ• ๋‹นํ•˜๋Š” ๊ฒƒ์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค

 

Property Observers

์ƒˆ ๊ฐ’์ด ์„ค์ • ๋ ๋•Œ๋งˆ๋‹ค ์ด๋ฅผ ๊ฐ์ง€ํ•  ์ˆ˜ ์žˆ๋Š” ์˜ต์ €๋ฒ„๋ฅผ ์ œ๊ณตํ•˜๋Š”๋ฐ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํŠน์ง•์„ ์ง€๋‹Œ๋‹ค.

 

1. ํ”„๋กœํผํ‹ฐ ์˜ต์ €๋ฒ„๋Š” ์ƒˆ ๊ฐ’์ด ์ด์ „ ๊ฐ’๊ณผ ๊ฐ™๋”๋ผ๋„ ํ•ญ์ƒ ํ˜ธ์ถœ๋œ๋‹ค.

2. lazy stored properties์—์„  ์‚ฌ์šฉ ํ•  ์ˆ˜ ์—†๋‹ค.

3. ์„œ๋ธŒ ํด๋ž˜์Šค์— ํ”„๋กœํผํ‹ฐ ์˜ต์ €๋ฒ„๋ฅผ ์ •์˜ํ•˜๋Š” ๊ฒƒ๋„ ๊ฐ€๋Šฅํ•˜๋‹ค.

4. ๊ณ„์‚ฐ๋œ ํ”„๋กœํผํ‹ฐ๋Š” setter์—์„œ ๊ฐ’์˜ ๋ณ€ํ™”๋ฅผ ๊ฐ์ง€ํ•˜๋ฏ€๋กœ ๋”ฐ๋กœ ์˜ต์ €๋ฒ„๋ฅผ ์ •์˜ํ•˜์ง€ ์•Š๋Š”๋‹ค.

 

ํ”„๋กœํผํ‹ฐ ์˜ต์ €๋ฒ„์—๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ ๋‘ ์˜ต์ €๋ฒ„๊ฐ€ ์žˆ๋‹ค!

  • willSet
  • didSet

willSet - ๊ฐ’์ด ์ €์žฅ๋˜๊ธฐ ์ „์— ํ˜ธ์ถœ

didSet - ์ƒˆ๋กœ์šด ๊ฐ’์ด ์ €์žฅ๋œ ํ›„์— ํ˜ธ์ถœ

 

willSet 

setter์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ํŒŒ๋ผ๋ฏธํ„ฐ ์ง€์ •ํ•˜์ง€ ์•Š์œผ๋ฉด ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ newValue ์‚ฌ์šฉ

 

didSet 

setter์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ํŒŒ๋ผ๋ฏธํ„ฐ ์ง€์ •ํ•˜์ง€ ์•Š์œผ๋ฉด ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ oldValue ์‚ฌ์šฉ

 

์˜ˆ์‹œ๋ฅผ ํ†ตํ•ด ์‚ดํŽด๋ณด์ž

 

class StepCounter {
    var totalSteps: Int = 0 {
        willSet(newTotalSteps) {
            print("About to set totalSteps to \(newTotalSteps)")
        }
        didSet {
            if totalSteps > oldValue  {
                print("Added \(totalSteps - oldValue) steps")
            }
        }
    }
}
let stepCounter = StepCounter()
stepCounter.totalSteps = 200
// About to set totalSteps to 200
// Added 200 steps
stepCounter.totalSteps = 360
// About to set totalSteps to 360
// Added 160 steps
stepCounter.totalSteps = 896
// About to set totalSteps to 896
// Added 536 steps

willSet๊ณผ didSet์˜ ๊ฐ’ ๋ณ€๋™์„ ํ†ตํ•ด ํ˜ธ์ถœ๋˜๋Š” ์ˆœ์„œ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

ํ”„๋กœํผํ‹ฐ ์˜ต์ €๋ฒ„๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ํ”„๋กœํผํ‹ฐ์˜ ๊ฐ’์ด ๋ฐ˜๋“œ์‹œ ์ดˆ๊ธฐํ™”๋˜์–ด ์žˆ์–ด์•ผ ํ•˜๋ฉฐ init()ํ•จ์ˆ˜ ์•ˆ์—์„œ ๊ฐ’ ํ• ๋‹น์‹œ์—๋Š” didSet๊ณผ willSet์ด ํ˜ธ์ถœ๋˜์ง€ ์•Š๋Š”๋‹ค.

 

willSet๊ณผ didSet์˜ ํ™œ์šฉ

๊ฐ€์žฅ ๋นˆ๋ฒˆํ•˜๊ฒŒ ์‚ฌ์šฉ๋˜๋Š” ๊ณณ์€ Model์—์„œ ๊ฐฑ์‹ ๋œ ๊ฐ’์„ View์—๊ฒŒ ๋ณด์—ฌ์ค„ ๋•Œ๋‹ค. ์ด๋•Œ ์‚ฌ์šฉํ•˜๋ฉด ์—ฌ๋Ÿฌ ์ฐธ์กฐ๋ฅผ ํ†ตํ•ด ๋งค๋ฒˆ ๊ฐ’์„ ๊ฐฑ์‹ ํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ์ž๋™์œผ๋กœ ๊ฐ’์„ ๊ฐฑ์‹ ํ•  ์ˆ˜ ์žˆ๋‹ค. 

 

Type Property

์•ž์„  ์ •์˜์—์„œ type property๋Š” ํ”„๋กœํผํ‹ฐ์™€ ํƒ€์ž…์„ ์—ฐ๊ฒฐํ•œ๋‹ค๊ณ  ํ•˜์˜€๋‹ค. ๋˜ํ•œ ์ธ์Šคํ„ด์Šค ํ”„๋กœํผํ‹ฐ๋Š” ์ธ์Šคํ„ด์Šค์— ์†ํ•œ ํ”„๋กœํผํ‹ฐ๋ฅผ ์˜๋ฏธํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ํƒ€์ž… ํ”„๋กœํผํ‹ฐ๋Š” ํƒ€์ž…์— ์†ํ•œ ํ”„๋กœํผํ‹ฐ๋‹ค. ํƒ€์ž…์— ํ•ด๋‹นํ•˜๋Š” ํ•˜๋‚˜์˜ ํ”„๋กœํผํ‹ฐ๋งŒ ์ƒ์„ฑํ•˜์—ฌ ๋ชจ๋“  ์ธ์Šคํ„ด์Šค์— ๊ณตํ†ต์œผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ๊ฐ’์„ ์ •์˜ํ•˜๊ณคํ•œ๋‹ค.

 

์˜ˆ์‹œ๋ฅผ ํ†ตํ•ด ์‚ดํŽด๋ณด์ž

struct SomeStructure {
    static var storedTypeProperty = "Some value."
    static var computedTypeProperty: Int {
        return 1
    }
}
enum SomeEnumeration {
    static var storedTypeProperty = "Some value."
    static var computedTypeProperty: Int {
        return 6
    }
}
class SomeClass {
    static var storedTypeProperty = "Some value."
    static var computedTypeProperty: Int {
        return 27
    }
    class var overrideableComputedTypeProperty: Int {
        return 107
    }
}

 

์˜คํ˜ธ .. ๋˜๊ฒŒ ์‹ ๊ธฐํ•˜๊ฒŒ ์ƒ๊ฒผ๋‹ค. ํด๋ž˜์Šค ์•„๋ž˜ ์„œ๋ธŒ ํด๋ž˜์Šค์—์„œ var(๋ณ€์ˆ˜)๋กœ ์„ ์–ธ๋˜์—ˆ๋‹ค. class ํƒ€์ž…์„ ๋”ฐ๋ฅด๊ณ  ์žˆ๊ณ  ์˜ค๋ฒ„๋ผ์ด๋“œ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.

 

๋Œ“๊ธ€