SwiftUI 지원EnclossingSelf 이야기

어느 날, 나는 불가사의한 기술을 발견하였다


응용 프로그램의 swift 창고를 보고 보지 못한 기술을 발견했다.
이것이 ↓
propertyWrapper입니다.그런데 wrappedValue를 방문하면 fatalError가 발매되고subscript~~의 수수께끼가 적혀 있습니다.
이subscript~~절(이하 EnclossingSelf)은 대단합니다.
아마도 Enclossing Self를 이용하여 SwiftUI의 Published 값을 업데이트함으로써 View의 재구성을 실현하는 메커니즘을 실현했기 때문일 것이다.
이번에는 이 Enclossing Self의 행동을 확인하고 SwiftUI에서 사용하는 Published를 자제했다.
@propertyWrapper
struct Observable<Value> {
  private var stored: Value
  ~~ 中略 ~~
  var wrappedValue: Value {
    get { fatalError("called wrappedValue getter") }
    set { fatalError("called wrappedValue setter") }
  }
  static subscript<EnclosingSelf>(
      _enclosingInstance observed: EnclosingSelf,
      wrapped wrappedKeyPath: ReferenceWritableKeyPath<EnclosingSelf, Value>,
      storage storageKeyPath: ReferenceWritableKeyPath<EnclosingSelf, Self>
    ) -> Value {
    get {
      observed[keyPath: storageKeyPath].stored
    }
    set {
      observed[keyPath: storageKeyPath].stored = newValue
    }
  }
}
https://github.com/apple/swift/blob/main/test/decl/var/property_wrappers_synthesis.swift

EnclossingSelf의 코코아는 대단하죠?


EnclossingSelf를 사용하면 가능한 일


다음 코드의 출력 결과는 어떻다고 생각합니까?
hoge 한 켤레만 있기 때문에 아무것도 출력되지 않을 거예요.
class Usage {
    @Wrapper var hoge: Int = 0
    func addOne() {
        hoge = 1
    }
    func fire() {
        print("fire!!")
    }
}
let usage = Usage()
usage.addOne()
집행 결과는 다음과 같다↓
호출되지 않은 Usage입니다.Fire () 를 호출하는 중입니다.
fire!!
Mirror를 사용해도 가능하지만 EnclossingSelf를 사용하면 이 행동을 간단하게 실현할 수 있다.

EnclossingSelf 설치 방법


위의 불가사의한 행동을 실현한propertywrapper.
위의 경우 EnclossingSelf에는 Usage가 있습니다.
  • wrappedValue라고 부르지 않는 get/set절.
    단, wrappedValue에 set 섹션이 없으면 EnclossingSelf는
    set 바이트가 있어도 set할 수 없습니다. 컴파일 오류가 발생했습니다.
  • @Wrapper가 있는 속성을 대입하면subscript 절을 실행합니다.
    enclossingSelf에 자신을 저장한 실례가 있습니다.
  • @propertyWrapper
    struct Wrapper<Value> {
        private var value: Value
        init(wrappedValue: Value) {
            value = wrappedValue
        }
        var wrappedValue: Value {
            get { fatalError() }
            set { fatalError() }
        }
        static subscript<EnclosingSelf>(
            _enclosingInstance enclosingSelf: EnclosingSelf,
            wrapped wrappedKeyPath: ReferenceWritableKeyPath<EnclosingSelf, Value>,
            storage storageKeyPath: ReferenceWritableKeyPath<EnclosingSelf, Self>
        ) -> Value {
            get {
                return enclosingSelf[keyPath: storageKeyPath].value
            }
            set {
                enclosingSelf[keyPath: storageKeyPath].value = newValue
                (enclosingSelf as? Usage)?.fire()   // Usage の fire を呼べる!!
            }
        }
    }
    

    Published를 해봤어요.


    SwiftUI는 Observable Object입니다.활동이 ObjectWillChage에서 재생되면 View가 업데이트됩니다.
    이↓ 프로그램을 실행하면 버튼을 누르면view가 업데이트되었는지 확인할 수 있습니다.
    ContentViewModel.action에서 새 값이 title에 대입되면 MyPublicied의 EnclossingSelf의subscript 섹션이 호출됩니다.ContentViewModel.활동이 object WillChage에서 재생되고 ContentView가 재구성됩니다.
    struct ContentView: View {
        @ObservedObject var viewModel = ContentViewModel()
        var body: some View {
            Button("\(viewModel.title)", action: viewModel.action)
        }
    }
    
    class ContentViewModel: ObservableObject {
        @MyPublished var title: String = "prepared"
        func action() {
            title = "start!!"
        }
    }
    
    @propertyWrapper
    public struct MyPublished<Value> {
        private var value: Value
        public init(wrappedValue: Value) {
            value = wrappedValue
        }
        public var wrappedValue: Value {
            get { fatalError() }
            set { fatalError() }
        }
        public static subscript<EnclosingSelf: ObservableObject>(
            _enclosingInstance object: EnclosingSelf,
            wrapped wrappedKeyPath: ReferenceWritableKeyPath<EnclosingSelf, Value>,
            storage storageKeyPath: ReferenceWritableKeyPath<EnclosingSelf, Self>
        ) -> Value {
            get {
                return object[keyPath: storageKeyPath].value
            }
            set {
                object[keyPath: storageKeyPath].value = newValue
                // 値が更新されたことを通知する
                (object.objectWillChange as? ObservableObjectPublisher)?.send()
            }
        }
    }
    

    최후


    가끔 swift 창고를 보면 모르는 기능이 많아서 즐거워요.

    좋은 웹페이지 즐겨찾기