Propertyclass, struct, enmeration에서 사용할 수 있다. 크게 Stored propertyComputed property로 구분할 수 있는데 Stored property는 class, struct에서만 사용할 수 있다.

추가적으로 Type property가 있다.

Property observer라는 것도 있는데 말 그대로 Property의 변화를 체크하는 기능이다.

Stored properties

struct FixedLengthRange {
  var firstValue: Int // = 0 등으로 바로 초기화도 가능하다.
  let length: Int // = 3 등으로 바로 초기화도 가능하다.
}

var rangeOfThreeItems = FixedLengthRange(firstValue: 0, Length: 3)
rangeOfThreeItems.firstValue = 6
rangeOfThreeItems.length = 5 // (x) let으로 선언되었음

Stored Properties of Constant Structure Instances

let rangeOfThreeItems = FixedLengthRange(firstValue: 0, Length: 3)
rangeOfThreeItems.firstValue = 6 // (x) rangeOfThreeItems이 let으로 선언됨

만약 FixedLengthRange가 class였다면 let으로 선언하더라도 내부 변수는 변경할 수 있다. class는 Reference type이기 때문이다.

Lazy Stored Properties

말 그대로 초기화를 늦게 하겠다는 것이다. 즉, 값이 접근되는 순간에 초기화한다는 뜻이다.

자세한 자료는 여기를 참고하면 된다.

애플 문서에서는 var은 항상 lazy로 선언하라고 하고 있다.

lazy를 사용하는 이유는 변수에 들어가는 값이 복잡하거나 계산의 비용이 많이 들어가는 경우에 사용하라고 한다. 당연히 초기화과정에서 시간이 오래 소모되면 프로그램 성능에 큰 영향을 미칠 것이다.

Computed properties

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
print("square.origin is now at (\(square.origin.x), \(square.origin.y))")
// Prints "square.origin is now at (10.0, 10.0)"

Shorthand Setter Declaration

위의 것을 아래처럼 짧게 사용할 수 있다.

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)
        }
    }
}

기본적으로 set에 아무 매개변수도 적지 않으면 newValue라는 값으로 사용할 수 있다.

Shorthand Getter Declaration

get도 아래처럼 줄일 수 있다. expression이 한줄이면 return값이 필요없다. (여기 참고)

struct CompactRect {
    var origin = Point()
    var size = Size()
    var center: Point {
        get {
            Point(x: origin.x + (size.width / 2),
                  y: origin.y + (size.height / 2))
        }
        set {
            origin.x = newValue.x - (size.width / 2)
            origin.y = newValue.y - (size.height / 2)
        }
    }
}

Read-Only Computed Properties

아래 처럼 get 키워드와 {}를 없애면 된다. 주의할 점은 항상 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

값의 입력을 모니터링 하여 따로 수행할 수 있는 작업을 할 수 있다.

lazy로 선언된 property이외에 모든 stored property는 property observer를 추가할 수 있다. property observer는 상속할 수 있다.

computed propertyget, set을 이용하여 값의 변화를 체크할 수 있기 때문에 property observer를 정의할 필요가 없다.

willSet : 값이 저장되기 전에 호출된다. 기본적으로 newValue라는 이름으로 사용할 수 있다. didSet : 새로운 값이 저장된 후 즉시 호출된다. 변경 전의 값은 oldValue라는 이름으로 사용할 수 있다.

in-out parameter에서 역시 didset, willset은 항상 호출된다. in-out parameter를 호출한 함수가 반환될 때 항상 해당 값을 갱신하기 때문이다.

실제 사용해본 코드를 살펴보면… 클래스업(ClassUp)에는 시간표 배경화면의 이미지를 설정하는 ViewController가 2개 존재한다. 두 Controller는 조금 역할이 다르지만 공통적으로 이미지를 사용한다. 이 이미지의 값이 변경될 때 마다 실제 배경에 적용하고 JPEG, GIF에 따라 type이 변해야 한다.

class TimetableBackgroundViewController: UIViewController {
    @IBOutlet weak var backView: FLAnimatedImageView! // 배경

    var backgroundExtensionType = 0
    var image: UIImage? {
        willSet {
            // To do something
        }
        didSet {
            self.backgroundExtensionType = 0
            self.backView.image = self.image
        }
    }

    var flaImage: FLAnimatedImage? {
        willSet {
            // To do something
        }
        didSet {
            self.backgroundExtensionType = 1
            self.backView.animatedImage = self.flaImage
        }
    }
}

// 존재하는 배경 수정할 경우
class PreviewSettingViewController: TimetableBackgroundViewController {
    override var image: UIImage? {
        willSet {
            // To do something
        }
        didSet {
            // 기존에 있던 배경 이미지를 처리하기
        }
    }
}

// 최초 배경 만들 경우
class SelectBackgroundViewController: TimetableBackgroundViewController {
    override var image: UIImage? {
        willSet {
            // To do something
        }
        didSet {
            // 최초 배경 만들 때 필요한 작업하기
        }
    }
}

클래스업(ClassUp)에 대해 자세히 설명하면 너무 길어지기에… 간략히 말하면 위와 같이 활용할 수 있다.

didSet 내부에서의 해당 값에 대한 변경은 다시 didSet을 호출하지 않는다.

Global and Local Variables

전역 변수, 지역 변수 모두 observer와 computed variable을 정의할 수 있다.

전역 변수에는 자체적으로 호출되어야지만 값을 할당하기 때문에 lazy를 사용할 필요가 없다. 지역 변수는 lazy를 사용할 수 없다.

Type Properties

간단하게 static 변수이다.

initializer을 가지지 않기 때문에 항상 기본 값을 할당해 주어야 한다.

자체적으로 lazy 속성을 가지기 때문에 처음 호출되는 경우에 할당된다.

struct SomeStructure {
    static var storedTypeProperty = "Some value."
    static var computedTypeProperty: Int {
        return 1
    }
    static var computedTypeReadWriteProperty: Int {
        get {
            return 1
        }
        set {
            newValue + 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
    }
}

위에서 보는 것과 같이 class 키워든는 자식 클래스에서 override 할 수 있다.