Можно ли Разрешить вызов didSet во время инициализации в 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, чтобы сделать вызовы обработки более краткими, но я бы предпочел просто обработать свойство в

8 151

8 ответов:

создайте собственный set-метод и используйте его в своем init-методе:

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

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

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

Если вы используете 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

как вариант ответа Оливера, вы можете обернуть строки в закрытие. Например:

class Classy {

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

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

}

Edit: ответ Брайана Вестфаля лучше имхо. Самое приятное в нем то, что он намекает на намерение.

У меня была та же проблема и это работает для меня

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
}

отметим, что Charlesism решение с закрытием всегда будет работать в этом случае. Так что мое решение-это просто альтернатива.

вы можете решить его в 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
    }
}