programing

스위프트에서 초기화 중에 didSet을 호출할 수 있습니까?

powerit 2023. 4. 28. 21:46
반응형

스위프트에서 초기화 중에 didSet을 호출할 수 있습니까?

질문.

애플의 문서는 다음과 같이 명시하고 있습니다.

willSet 및 didSet 관찰자는 속성을 처음 초기화할 때 호출되지 않습니다.속성 값이 초기화 컨텍스트를 벗어나 설정된 경우에만 호출됩니다.

초기화 중에 강제로 호출할 수 있습니까?

왜요?

제가 이 수업을 한다고 치자.

class SomeClass {
    var someProperty: AnyObject {
        didSet {
            doStuff()
        }
    }

    init(someProperty: AnyObject) {
        self.someProperty = someProperty
        doStuff()
    }

    func doStuff() {
        // do stuff now that someProperty is set
    }
}

메소드를 만들었습니다.doStuff통화 처리를 보다 간결하게 하기 위해, 하지만 나는 차라리 내의 속성을 처리하고 싶습니다.didSet기능.초기화 중에 강제로 호출할 수 있는 방법이 있습니까?

갱신하다

나는 단지 우리반의 intializer의 편의성을 제거하고 초기화 후에 당신에게 속성을 설정하도록 강요하기로 결정했습니다.이것은 제가 알 수 있게 해줍니다.didSet항상 호출됩니다.이게 전체적으로 더 좋은지는 결정하지 못했지만, 제 상황에 잘 맞습니다.

사용하는 경우defer선택적 속성을 업데이트하거나 이미 초기화한 비선택적 속성을 추가로 업데이트하기 위해 이니셜라이저 내부에서super.init()메소드, 그리고 당신의willSet,didSet등이 호출됩니다.저는 이것이 적절한 장소에서 통화를 추적해야 하는 별도의 방법을 구현하는 것보다 더 편리하다고 생각합니다.

예:

public class MyNewType: NSObject {

    public var myRequiredField:Int

    public var myOptionalField:Float? {
        willSet {
            if let newValue = newValue {
                print("I'm going to change to \(newValue)")
            }
        }
        didSet {
            if let myOptionalField = self.myOptionalField {
                print("Now I'm \(myOptionalField)")
            }
        }
    }

    override public init() {
        self.myRequiredField = 1

        super.init()

        // Non-defered
        self.myOptionalField = 6.28

        // Defered
        defer {
            self.myOptionalField = 3.14
        }
    }
}

산출량:

I'm going to change to 3.14
Now I'm 3.14

고유한 set-Method를 만들고 init-Method 내에서 사용합니다.

class SomeClass {
    var someProperty: AnyObject! {
        didSet {
            //do some Stuff
        }
    }

    init(someProperty: AnyObject) {
        setSomeProperty(someProperty)
    }

    func setSomeProperty(newValue:AnyObject) {
        self.someProperty = newValue
    }
}

선언함으로써someProperty유형:AnyObject!(암묵적으로 포장되지 않은 선택사항), 사용자는 사용자 자신이 사용하지 않고 완전히 초기화할 수 있도록 허용합니다.someProperty설정되어 있는전화할 때setSomeProperty(someProperty)당신은 그것과 동등한 것을 부르고 있습니다.self.setSomeProperty(someProperty)일반적으로 셀프가 완전히 초기화되지 않았기 때문에 이 작업을 수행할 수 없습니다.부터someProperty초기화할 필요가 없으며 사용자가 자신에 의존하는 메서드를 호출하는 경우 Swift가 초기화 컨텍스트를 떠나 didSet이 실행됩니다.

올리버의 대답의 변형으로, 당신은 그 선들을 폐쇄로 포장할 수 있습니다.예:

class Classy {

    var foo: Int! { didSet { doStuff() } }

    init( foo: Int ) {
        // closure invokes didSet
        ({ self.foo = foo })()
    }

}

편집: 브라이언 웨스트팔의 대답은 더 친절합니다.그의 좋은 점은 그것이 의도를 암시한다는 것입니다.

저도 같은 문제가 있었고 이것은 저에게 효과가 있습니다.

class SomeClass {
    var someProperty: AnyObject {
        didSet {
            doStuff()
        }
    }

    init(someProperty: AnyObject) {
        defer { self.someProperty = someProperty }
    }

    func doStuff() {
        // do stuff now that someProperty is set
    }
}

하위 클래스에서 이 작업을 수행하면 작동합니다.

class Base {

  var someProperty: AnyObject {
    didSet {
      doStuff()
    }
  }

  required init() {
    someProperty = "hello"
  }

  func doStuff() {
    print(someProperty)
  }
}

class SomeClass: Base {

  required init() {
    super.init()

    someProperty = "hello"
  }
}

let a = Base()
let b = SomeClass()

a예,didSet트리거되지 않습니다.하지만b예,didSet하위 클래스에 있기 때문에 트리거됩니다.그것은 무엇과 관련이 있습니다.initialization context로, 이 에는 말로경, 는에우이정을합니다.superclass그것에 대해 신경을 썼습니다.

이것이 해결책은 아니지만, 클래스 생성자를 사용하는 것이 대안이 될 수 있습니다.

class SomeClass {
    var someProperty: AnyObject {
        didSet {
            // do stuff
        }
    }

    class func createInstance(someProperty: AnyObject) -> SomeClass {
        let instance = SomeClass() 
        instance.someProperty = someProperty
        return instance
    }  
}

호출하려는 특정한 경우willSet또는didSet東京의 init슈퍼 클래스에서 사용할 수 있는 속성의 경우 슈퍼 속성을 직접 할당하기만 하면 됩니다.

override init(frame: CGRect) {
    super.init(frame: frame)
    // this will call `willSet` and `didSet`
    someProperty = super.someProperty
}

이러한 경우에는 폐쇄가 있는 샤를리즘 솔루션도 항상 작동합니다.그래서 저의 해결책은 그저 대안일 뿐입니다.

유감스럽게도 루트 클래스가 초기화될 때 세트 관찰자가 호출되지 않았습니다.

클래스가 하위 클래스가 아닌 경우, 원하는 기능을 달성하기 위해 게터와 세터를 사용해야 합니다.

class SomeClass {
    private var _test: Int = 0
    var test: Int {
        get { _test }
        set { _test = newValue }
    }
    
    init(test: Int) { self.test = test }
}

또는 클래스가 하위 클래스인 경우 didSet을 사용하여 다음 작업을 수행할 수 있습니다.

override init(test: int) {
    super.init()
    self.test = test
}

super.init()가 호출된 후에 didSet이 호출되어야 합니다.

시도하지 않았지만 효과가 있을 수도 있는 한 가지:

init(test: int) {
    defer { self.test = test }
}

참고: 속성을 사용할 수 없도록 설정하거나 속성의 기본값을 설정하거나 클래스 속성의 래핑을 해제해야 합니다.

obj-discovery 방식으로 해결할 수 있습니다.

class SomeClass {
    private var _someProperty: AnyObject!
    var someProperty: AnyObject{
        get{
            return _someProperty
        }
        set{
            _someProperty = newValue
            doStuff()
        }
    }
    init(someProperty: AnyObject) {
        self.someProperty = someProperty
        doStuff()
    }

    func doStuff() {
        // do stuff now that someProperty is set
    }
}

언급URL : https://stackoverflow.com/questions/25230780/is-it-possible-to-allow-didset-to-be-called-during-initialization-in-swift

반응형