Properties

  • 변수의 다른 이름이다.
  • class, structure, enumeration 에서 전체의 속성으로 사용되는 변수를 프로퍼티라 부른다.
  • 저장 프로퍼티 (Stored Properties)
  • 연산 프로퍼티 (Computed Properties)
  • 타입 프로퍼티 (Type Properties)


Stored Properties (저장 프로퍼티)

  • 가장 일반적인 프로퍼티 이다.
  • 값을 저장하는 용도로 사용된다.
  • 특정한 class, structure에서 인스턴스로 변수나 상수로 값을 저장한다.
  • 초기값을 설정할 수 있다.
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

let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4)
// this range represents integer values 0, 1, 2, and 3

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

rangeOfFourItems 은 let으로 선언되었기 때문에 firstValue가 variable이더라도 값을 변경할 수 없다. 이것은 structure가 value types이기 때문이다. value types은 프로퍼티를 포함하여 상수로 표현되기 때문이다. 하지만 reference types인 class는 그렇지 않다. reference types의 인스턴스를 상수로 선언하더라도 인스턴스의 variable properties는 값을 변경할 수 있다.


Lazy Stored Properties(지연 저장 프로퍼티)

  • 처음 프로퍼티가 사용되기 전까지 초기값이 계산되지 않는 특성을 가지고 있는 프로퍼티 이다.
  • 지연 저장 프로퍼티는 lazy keyword를 선언 앞에 사용한다.
  • let 은 지연 저장 프로퍼티로 설정할 수 없다.
  • 초기값이 외부 요소와 관련이 있어 인스턴스 초기화가 완료될때 까지 값을 모를때 사용하면 유용하다.
  • 초기화 하는데 오래걸리거나, 복잡한 초기화 과정이 있는 변수의 경우 사용하면 유용하다.
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 has not yet been created

print(manager.importer.filename)
// the DataImporter instance for the importer property has now been created
// Prints "data.txt"


Computed Properties (연산 프로퍼티)

  • 실제로 값을 저장하지 않고 getter와 optional setter를 통해 값을 간접적으로 설정하거나 받을 수 있다.
  • 일반적으로 메소드 중 속성에 관련된 메소드를 연산 프로퍼티로 사용한다.
  • class, structure, enumeration 에서 사용이 가능하다.
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))")
// Prints "square.origin is now at (10.0, 10.0)"

Setter ValueName 미지정

setter의 값이름 미지정시 newValue가 기본값으로 사용된다.

struct AlternativeRect {
    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 {
            origin.x = newValue.x - (size.width / 2)
            origin.y = newValue.y - (size.height / 2)
        }
    }
}

Read-Only Computed Properties

setter 없이 getter만 있는 연산 프로퍼티를 Read-Only 연산 프로퍼티라고 한다. get 키워드 없이 바로 작성할 수 있으며 언제나 값을 return해야 한다. 연산프로퍼티는 기본적으로 var 키워드를 통한 변수로 선언해야 한다. 그 이유는 프로퍼티의 값이 고정되어 있지 않기 때문이다.

struct Cuboid {
    var width = 0.0, height = 0.0, depth = 0.0
    var volume: Double {
        return width * height * depth
    }
}

let fourByFiveByTwo = Cuboid(width: 4.0, height: 5.0, depth: 2.0)
print("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)")
// Prints "the volume of fourByFiveByTwo is 40.0"


Property Observers

  • Property Observers는 프로퍼티의 값의 변화를 감시하고 그에 따라 대응한다.
  • 프로퍼티의 값이 설정될 때마다 호출되며 ,현재 값과 같은 값이라도 호출된다.
  • 초기값이 설정된 저장 프로퍼티에서 사용 가능하지만 지연 저장 프로퍼티는 제외된다.
  • 어떤 상속된 프로퍼티도 오버라이딩을 통해 Property Observers가 사용 가능하다.
  • willSet, didSet 키워드를 통해 값 변화 전,후를 알 수 있다.
  • 값 이름 미지정시 willSet - newValue, didSet - oldValue가 기본값으로 지정된다.

superclass 프로퍼티의 willSet,didSet 옵저버는 superclass initializer가 호출된 후 subclass initializer안에서 프로퍼티 값이 세팅될때 호출된다.

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


Global and Local Variables

전역변수는 function, method, closure, or type context의 밖에서 선언된 변수이며 지역변수는 function, method, closure의 안에서 선언된 변수이다. 일반적으로 사용하는 전역, 지역변수는 Stored Properties를 이야기 하지만 Computed Properties 또한 Global,Local 범위에서 Property Observers를 선언할 수 있다.


Type Properties

  • 인스턴스의 속성이 아닌, 타입에 따른 속성을 정의 할 수 있다.
  • static 키워드를 사용해서 type properties를 설정할 수 있다.
  • 클래스의 경우 연산 프로퍼티의 오버라이드를 지원하기 위해서는 class 키워드를 사용해야 한다.
  • class 키워드는 저장 프로퍼티는 사용할 수 없다.
  • 값을 가져올때는 클래스의 이름을 통해 가져올 수 있다.
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
    }
}

print(SomeStructure.storedTypeProperty)
// Prints "Some value."

SomeStructure.storedTypeProperty = "Another value."
print(SomeStructure.storedTypeProperty)
// Prints "Another value."

print(SomeEnumeration.computedTypeProperty)
// Prints "6"

print(SomeClass.computedTypeProperty)
// Prints "27"


Querying and Setting Type Properties

Type Properties는 dot syntax를 통해 인스턴스의 프로퍼티와 가티 조회, 설정이 가능하다. 하지만 타입 자체가 조회, 설정되는 것이지 그 타입의 인스턴스가 되는 것이 아니다.

아래 예제는 오디오 레벨 미터를 모델링하기 위해 두개의 채널을 결합하는 것이다. 오디오 레벨이 0이면 해당 채널의 표시등이 켜지지 않고 10이면 모든 표시등이 켜진다. 이 그림에서 왼쪽 채널 레벨은 9이고 오른쪽 채널 레벨은 7이다.

struct AudioChannel {
    static let thresholdLevel = 10
    static var maxInputLevelForAllChannels = 0

    var currentLevel: Int = 0 {
        didSet {
            if currentLevel > AudioChannel.thresholdLevel {
                // cap the new audio level to the threshold level
                currentLevel = AudioChannel.thresholdLevel
            }

            if currentLevel > AudioChannel.maxInputLevelForAllChannels {
                // store this as the new overall maximum input level
                AudioChannel.maxInputLevelForAllChannels = currentLevel
            }
        }
    }
}


var leftChannel = AudioChannel ()
var rightChannel = AudioChannel ()

leftChannel . currentLevel = 7
print ( leftChannel . currentLevel )
// Prints "7"
print ( AudioChannel . maxInputLevelForAllChannels )
// Prints "7"

rightChannel . currentLevel = 11
print ( rightChannel . currentLevel )
// Prints "10"
print ( AudioChannel . maxInputLevelForAllChannels )
// Prints "10"

currentLevel의 newValue가 타입의 thresholdLevel 보다 큰 경우 peoperty observer가 currentLevel 을 thresholdLevel의 값으로 대체한다.