lunchScreen / Interview_Questions

기술면접을 준비하는 버디들
68 stars 10 forks source link

property wrapper에 대해서 설명하시오. #100

Open tmfrlrkvlek opened 2 years ago

duyeonnn commented 2 years ago

프로퍼티 래퍼는 프로퍼티가 저장되는 방식을 관리하는 코드와 프로퍼티가 정의되는 코드 사이에 분리된 계층을 추가해 준다. 프로퍼티를 정의할 때마다 매번 로직을 작성해야하는 보일러 플레이트 코드를 줄일 수 있고 어노테이션만 사용하면 바로 로직을 적용할 수 있어서 유용하게 쓰일 수 있따.

@propertyWrapper
struct TwelveOrLess {
    private var number: Int

    init() {
        self.number = 0
    }

    var wrappedValue: Int {
        get { number }
        set { number = min(newValue, 12) }
    }
}

struct SmallRectangle {
        @TwelveOrLess var height: Int
        @TwelveOrLess var width: Int
}

var rectangle = SmallRectangle()
print(rectangle.height) // 0
rectangle.height = 10
print(rectangle.height) // 10
rectangle.height = 24
print(rectangle.height) // 12

wrappedValue가 반드시 필요하다.

property wrapper에 argument를 포함하는 방식도 가능하다.

@propertyWrapper
struct SmallNumber {
    private var maximum: Int
    private var number: Int

    var wrappedValue: Int {
        get { return number }
        set { number = min(newValue, maximum) }
    }

    init() {
        maximum = 12
        number = 0
    }

    init(wrappedValue: Int) {
        maximum = 12
        number = min(wrappedValue, maximum)
    }

    init(wrappedValue: Int, maximum: Int) {
        self.maximum = maximum
        number = min(wrappedValue, maximum)
    }
}

struct NarrowRectangle {
    @SmallNumber(wrappedValue: 2, maximum: 5) var height: Int
    @SmallNumber(wrappedValue: 3, maximum: 4) var width: Int
}

var narrowRectangle = NarrowRectangle()
print(narrowRectangle.height, narrowRectangle.width)
// Prints "2 3"

narrowRectangle.height = 100
narrowRectangle.width = 100
print(narrowRectangle.height, narrowRectangle.width)
// Prints "5 4"

또한 wrapped value 뿐만 아니라 projected value를 추가하여 기능을 확장할 수 있다. projected value를 활용하여 값을 조정 했는지 알게 할 수 있다. 달러($) sign으로 접근할 수 있다.

@propertyWrapper
struct SmallNumber {
    private var number: Int
    private(set) var projectedValue: Bool

    var wrappedValue: Int {
        get { return number }
        set {
            if newValue > 12 {
                number = 12
                projectedValue = true
            } else {
                number = newValue
                projectedValue = false
            }
        }
    }

    init() {
        self.number = 0
        self.projectedValue = false
    }
}

struct SomeStructure {
    @SmallNumber var someNumber: Int
}
var someStructure = SomeStructure()

someStructure.someNumber = 4
print(someStructure.$someNumber)
// Prints "false"

someStructure.someNumber = 55
print(someStructure.$someNumber)
// Prints "true"

projected value는 어떤 타입이던 가능하기 때문에 다른 정보들도 노출시킬 수 있다.

inuinseoul commented 2 years ago

반복적으로 필요로하는 property 구현 패턴(getter,setter)에 대한 집합을 컴파일러에 하드코딩해놓고, 이를 라이브러리로 정의할 수 있는 일반적인 메커니즘을 제공하는 기능입니다.

Property wrapper는 프로퍼티가 저장되는 방식을 관리하는 코드와 프로퍼티를 정의하는 코드 사이에 새로운 분리 계층을 추가합니다. 개발을 진행하다보면 Thread-safe한 프로퍼티 또는 값을 DB에 저장하는 프로퍼티가 여러개 필요한 경우가 있을 것입니다. 이를 위해서는 각각의 프로퍼티 모두에 해당 처리들을 위한 코드를 작성해야 했습니다. Property wrapper를 사용하게되면 이런 값 저장을 관리하는 코드들을 Property wrapper 정의부에서 한번만 작성하면 됩니다. 그 후에는 필요한 프로퍼티에 Property wrapper를 가져다 적용하기만 하면 됩니다.

sustainable-git commented 2 years ago
import Foundation

@propertyWrapper
struct Uppercase {
    private var value: String = ""

    var wrappedValue: String {
        get { self.value }
        set { self.value = newValue.uppercased() }
    }
}

struct Address {
    @Uppercase var town: String
    init(town: String) {
        self.town = town
    }
}

let address = Address(town: "seoul")
print(address.town) // "SEOUL"