簡體   English   中英

是否可以允許在 Swift 初始化期間調用 didSet?

[英]Is it possible to allow didSet to be called during initialization in Swift?

問題

Apple 的文檔規定:

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函數中的屬性。 有沒有辦法在初始化期間強制調用它?

更新

我決定只刪除我的類的便利初始化器,並強制您在初始化后設置屬性。 這讓我知道didSet將始終被調用。 我還沒有決定這是否總體上更好,但它很適合我的情況。

如果您在初始化程序中使用defer來更新任何可選屬性或進一步更新您已經初始化並且在調用任何super.init()方法之后的非可選屬性,那么您的willSetdidSet等將叫做。 我發現這比實現單獨的方法更方便,您必須在正確的位置跟蹤調用。

例如:

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! (隱式展開的可選),您允許 self 完全初始化而無需設置someProperty 當您調用setSomeProperty(someProperty)您正在調用self.setSomeProperty(someProperty)的等效項。 通常你不能這樣做,因為 self 還沒有完全初始化。 由於someProperty不需要初始化並且您正在調用依賴於 self 的方法,因此 Swift 離開初始化上下文並且 didSet 將運行。

作為 Oliver 答案的一種變體,您可以將這些行包裹在一個閉包中。 例如:

class Classy {

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

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

}

編輯:Brian Westphal 的回答更好,恕我直言。 他的好處在於它暗示了意圖。

我有同樣的問題,這對我有用

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
    }  
}

在特定情況下,您要在init為超類中可用的屬性調用willSetdidSet ,您可以簡單地直接分配您的超屬性:

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

請注意,在這種情況下,帶有閉包的Charlesism解決方案也始終有效。 所以我的解決方案只是一個替代方案。

不幸的是,初始化根類時不會調用 didSet 觀察者。

如果您的類不是子類,則必須使用 getter 和 setter 來實現您想要的功能:

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
}

didSet 應該在 super.init() 被調用后被調用。

一件事我還沒有嘗試過,但可能也有效:

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

注意:您需要使您的屬性可以為空,或為它們設置默認值,或解開類屬性。

你可以用obj-с的方式解決它:

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
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM