관리 메뉴

김종권의 iOS 앱 개발 알아가기

[iOS - SwiftUI] @State를 사용하면 mutating 없이도 수정이 가능한 원리 (@State 직접 구현해보기) 본문

iOS 응용 (SwiftUI)

[iOS - SwiftUI] @State를 사용하면 mutating 없이도 수정이 가능한 원리 (@State 직접 구현해보기)

jake-kim 2024. 7. 19. 01:32

@State를 사용하면 mutating 없이도 사용이 가능

  • 아래처럼 @State를 프로퍼티에 붙여서 사용하면 change()함수에서 mutating 키워드가 없더라도 문제없이 동작
struct MyStruct {
    @State
    var val = 0

    func change() {
        val = 0
    }
}
  • struct이기 때문에 값을 변경하려면 원래 mutating키워드가 필요하지만 @State를 사용하면 mutating 없이 사용이 가능
struct MyStruct {
    var a1 = 0
    
    func change() {
        a1 = 2 // Cannot assign to property: 'self' is immutable
    }
}
  • computed property, 일반적인 property wrapper모두 mutating이 필요함
struct MyStruct {
    @DefaultValue(wrappedValue: 0, defaultValue: 18)
    var age: Int
    
    var a1 = 0
    
    var a2: Int {
        get { a1 }
        set { a1 = newValue }
    }
    
    func change() {
        age = 1 // Cannot assign to property: 'self' is immutable
        a1 = 2 // Cannot assign to property: 'self' is immutable
        a2 = 3 // Cannot assign to property: 'self' is immutable
    }
}

@propertyWrapper
struct DefaultValue<T> {
    private var value: T
    private let defaultValue: T

    init(wrappedValue: T, defaultValue: T) {
        self.value = wrappedValue
        self.defaultValue = defaultValue
    }

    var wrappedValue: T {
        get { return value }
        set { value = newValue }
    }

    var projectedValue: T {
        get { return defaultValue }
    }
}

@State를 사용하면 mutating이 없어도 되는 이유

  • @State는 property wrapper인데, property wrapper 내부에서 관리하는 value를 reference type으로 설정하면 가능
    • SwiftUI의 @State처럼 만들면 아래처럼 property wrapper 의 value가 class타입으로 정의
private class StateStorage<Value>: ObservableObject {
    @Published var value: Value
    
    init(initialValue: Value) {
        self.value = initialValue
    }
}

@propertyWrapper
struct SimpleState<Value>: DynamicProperty {
    @ObservedObject private var stateStorage: StateStorage<Value>
    
    init(wrappedValue: Value) {
        self.stateStorage = StateStorage(initialValue: wrappedValue)
    }
    
    var wrappedValue: Value {
        get { stateStorage.value }
        nonmutating set { stateStorage.value = newValue }
    }
    
    var projectedValue: Binding<Value> {
        Binding(
            get: { self.wrappedValue },
            set: { self.wrappedValue = $0 }
        )
    }
}
  • 직접 만든 @SimpleState를 사용하면 mutating이 없어도 에러 없이 사용가능
struct MyStruct {
    @SimpleState
    var a3 = 0
    
    func change() {
        a3 = 4
    }
}

 

Comments