소소한개발팁
article thumbnail
반응형

Properties 

- Properties 은 클래스, 구조체, 열거형과 관련된 값입니다. 특성의 종류에는 Stored Properties와 Computed Properties 있습니다.

 

- Stored Properties 은 값을 저장하고 있는 프로퍼티이며,  Computed Properties 값을 저장하고 있지 않고 특정하게 계산한 값을 반환해 주는 프로퍼티입니다.

 

- Computed Properties 은 클래스, 구조체, 열거형 모두에서 사용 가능하지만, Stored Properties  클래스와 구조체에서만 사용 가능하며 프로퍼티 옵서버를 정의해서 값이 변할 때마다 모니터링할 수 있습니다.

 

 

Stored Properties 

- Stored Properties는 위에서 설명한 대로 단순히 값을 저장하고 있는 프로퍼티입니다. 이 프로퍼티 let(상수) 및 var(변수)로 선언하여 사용할 수 있습니다.

 

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

 

- 위 예제를 보면 firstValue와 lengthlength에 첫 값과 그 길이를 각각의 프로퍼티에 저장해 범위 값을 표현합니다.

 

 

Stored Properties of Constant Structure Instances

- 구조체를 상수로 선언하면 구조체 인스턴스의 프로퍼티를 변경할 수 없습니다.

 

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 let으로 선언하더라도 프로퍼티를 변경 가능합니다. 왜냐하면 클래스 인스턴스는 참조 타입 이기 때문입니다.

 

 

 Lazy Stored Properties 

- Lazy Stored Properties는 값이 처음으로 사용되기 전에는 계산되지 않는 프로퍼티입니다. Lazy Stored Properties로 선언하기 위해서는 프로퍼티의 선언 앞에 lazy 키워드를 붙이면 됩니다.

 

- Lazy Stored Properties는 반드시 변수로 선언해야 합니다. 왜냐하면 상수는 초기화가 되기 전에 항상 값을 같은 property인데, 지연 프로퍼티는 처음 사용되기 전에는 값을 갖지 않는 프로퍼티이기 때문입니다.

 

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

 

- DataManager라는 클래스를 선언하고 이 클래스는 데이터를 가져오는 DataImporter클래스를 갖고 있습니다. 그리고 이 DataImporter는 실제 디스크 파일에서 데이터를 가져오기 때문에 초기화에 많은 시간이 걸립니다.

 

- 그러므로 이 클래스를 지연 프로퍼티(lazy var importer = DataImporter()) 로 선언해야 합니다.

 

- 이 프로퍼티는 코드에서 볼 수 있듯 DataManager 인스턴스 manager를 생성하고 거기에 data를 넣어도 그 시점에 DataImporter인스턴스는 생성되어 있지 않습니다. 지연 프로퍼티로 선언해 놓았기 때문에 실제 그 프로퍼티를 사용하기 전에는 복잡하고 시간 이 많이 필요로 하는 연산을 할 필요가 없습니다.

 

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

 

- manager.importer.filename가 실행되어 실제로 importer 프로퍼티에 처음 접근할 때 importer 인스턴스는 생성됩니다.

 

 

Computed Properties 

- Stored Properties 뿐만 아니라 추가적으로 클래스, 구조체, 열거형은 Computed Properties를 선언할 수 있습니다. 이 Computed Properties는 실제 값을 저장하고 있는 것이 아니라 getter와 optional 한 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))")
// Prints "square.origin is now at (10.0, 10.0)"

 

- 위 코드는 좌표와 크기를 갖는 사각형을 표현하는 구조체에 관한 코드입니다.

- 여기서 Rect 구조체는 사각형의 중점을 표현하는 center center라는 계산된 프로퍼티를 제공합니다.

- 이 프로퍼티는 계산된 프로퍼티의 정의대로 값을 직접 갖고 있는 것이 아니라 다른 좌표와 크기 프로퍼티들을 적절히 연산해서 구할 수 있습니다.

- setset으로 사각형의 중점을 직접 설정할 수 있는데, 이 값을 설정할 때 x, y좌표가 어떤 값을 가져야 하는지 계산해서 x, y에 적절한 좌표값을 넣어 줍니다.

 

 

 

Shorthand Setter Declaration 

- Setter의 인자 이름을 아래와 같이 set(newCenter)라고 명시했지만, 만약 이렇게 (newCenter)라고 인자 이름을 지정하지 않으면 인자 기본 이름인 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)
        }
    }
}

 

- set메서드 안에서 인자 이름을 지정하지 않았는데도 newValue.x, newValue.y를 사용 가능한 것을 확인할 수 있습니다.

 

 

Read-Only Computed Properties 

- getter만 있고 setter를 제공하지 않는 계산된 프로퍼티를 Read-Only Computed Properties라고 합니다.

 

- Read-Only Computed Properties는 반드시 반환 값을 제공하고 다른 값을 지정할 수는 없는 프로퍼티입니다.

 

- 계산된 프로퍼티를 선언 시에는 반드시 상수가 아니라 변수로 선언해야 합니다.

 

- 보통 읽기 전용(read-only)이라 함은 한번 값이 정해지면 변하지 않기 때문에 상수로 선언하는 것이 맞으나 계산된 프로퍼티는 읽기 전용(read-only)이라 하더라도 계산 값에 따라 값이 변할 수 있기 때문에 변수로 선언합니다.

 

- volume이라는

 

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 

- 프로퍼티에는 새 값이 set 될 때마다 이벤트를 감지할 수 있는 옵서버를 제공합니다. 이것을 Property Observers 라 하는데 Property Observers는 새 값이 이전 값과 같더라도 항상 호출됩니다.

 

- Property Observers는 Lazy Stored Properties에서는 사용할 수 없습니다. 

 

- 서브클래스의 프로퍼티에 옵서버를 정의하는 것이 가능합니다.

 

- Computed Properties에서는 setter 값의 변화를 감지할 수 있어서 옵서버를 정의할 필요가 없습니다.

 

- 프로퍼티에서는 2가지의 옵서버를 제공합니다.

 

1. willSet : 값이 저장되기 바로 직전에 호출

  - willSet에서는 newValue를 사용합니다.

2. didSet : 새 값이 저장되고 난 직후에 호출

  - didSet에서는 oldValue를 사용합니다.

 

- 서브클래스에서 특정 프로퍼티의 값을 설정했을 때, 슈퍼클래스의 초기자가 호출된 후 willSet, didSet 프로퍼티 옵서버가 실행됩니다. 슈퍼클래스에서 프로퍼티를 변경하는 것도 마찬가지로 슈퍼클래스의 초기자가 호출된 후 옵서버가 실행됩니다. 아래는 Property Observers의 예시입니다.

 

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

 

- 만약 in-out 파라미터로 선언된 함수의 인자에 프로퍼티를 넘기면 willSet 및 didSet이 항상 실행됩니다. 이유는 in-out 파라미터이기 때문에 프로퍼티가 항상 복사되기 때문입니다. 이 in-out 파라미터의 프로퍼티는 항상 원래 값에 새 값을 다시 덮어쓰게 됩니다.

 

 

Type Property

- 인스턴스 프로퍼티는 특정 인스턴스에 속한 프로퍼티를 말합니다. 이 프로퍼티는 새로운 인스턴스가 생성될 때마다 새로운 프로퍼티도 같이 생성됩니다. 타입 프로퍼티는 특정 타입에 속한 프로퍼티로 그 타입에 해당하는 단 하나의 프로퍼티만 생성됩니다.

 

 

Type Property Syntax 

- 타입 프로퍼티를 선언을 위해서는 static 키워드를 사용합니다. 클래스에서는 staticclass 이렇게 2가지 형태로 타입 프로퍼티를 선언할 수 있는데 두 가지 경우의 차이는 서브클래스에서 overriding가능 여부입니다. 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
    }
}

 

Querying and Setting Type Properties

- 인스턴스 프로퍼티와 마찬가지로 타입 프로퍼티도 점 연산자(dot operator)로 프로퍼티의 값을 가져오고 할당할 수 있습니다. 예시는 아래와 같습니다.

 

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"

 

 

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"

 

 

내용은 https://docs.swift.org/swift-book/LanguageGuide/TheBasics.html를 보면서 작성하였고 원문으로 작성된 내용을 옮기다 보니 이상한 부분이 있을 수 있습니다. 자세한 내용은 위의 링크를 확인해주시기 바랍니다.

반응형
profile

소소한개발팁

@개발자 뱅

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