是否可以在Swift初始化期间允许调用didSet?

时间:2014-08-10 16:38:57

标签: ios swift didset

问题

Apple's docs指定:

  首次初始化属性时,不会调用

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。我还没有决定这总体上是否更好,但它很适合我的情况。

9 个答案:

答案 0 :(得分:240)

如果您在初始化程序中使用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

答案 1 :(得分:87)

创建一个自己的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
    }
}

答案 2 :(得分:72)

作为Oliver答案的变体,你可以将这些行包裹在一个闭包中。例如:

class Classy {

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

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

}

编辑:Brian Westphal的回答是更好的imho。关于他的好处是它暗示了意图。

答案 3 :(得分:11)

我遇到了同样的问题,这对我有用

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

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

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

答案 4 :(得分:1)

虽然这不是一个解决方案,但另一种方法是使用类构造函数:

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

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

答案 5 :(得分:1)

如果您在子类

中执行此操作,则此方法有效
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确实关心那个

答案 6 :(得分:1)

如果您要在willSet内调用didSetinit来获取超类中可用的属性,您可以直接分配您的超级属性:

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

请注意,带有闭包的Charlesism解决方案在这种情况下也总能正常工作。所以我的解决方案只是一种替代方案。

答案 7 :(得分:0)

不幸的是,初始化根类时没有调用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()之后被调用。

我没有尝试过的一件事,但MIGHT也可以工作:

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

注意::您将需要使您的属性可为空,或者为它们设置默认值,或者解开类的属性。

答案 8 :(得分:-1)

你可以用对象的方式解决它:

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