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

Log*_*gan 199 ios swift didset

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
    }
}
Run Code Online (Sandbox Code Playgroud)

我创建了方法doStuff,使处理调用更简洁,但我宁愿只处理didSet函数中的属性.有没有办法在初始化期间强制调用它?

更新

我决定只为我的类删除便利的intializer并强制你在初始化后设置属性.这让我知道didSet将永远被召唤.我还没有决定这总体上是否更好,但它很适合我的情况.

Bri*_*hal 276

如果您使用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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

将产量:

I'm going to change to 3.14
Now I'm 3.14
Run Code Online (Sandbox Code Playgroud)

  • 厚颜无耻的小伎俩,我喜欢它......虽然Swift奇怪怪癖. (5认同)
  • 大.你找到了另一个使用`defer`的有用案例.谢谢. (4认同)
  • 非常有趣..我之前从未见过使用过defer。雄辩而有效。 (3认同)
  • 延迟的大用处!希望我能给你不止一票。 (2认同)

Oli*_*ver 96

创建一个自己的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
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 我想我知道这里发生了什么.通过将`someProperty`声明为类型:`AnyObject!`(一个隐式解包的可选),允许`self`完全初始化而不设置`someProperty`.当你调用`setSomeProperty(someProperty)时,你正在调用`self.setSomeProperty(someProperty)`的等价物.通常你不能这样做,因为`self`还没有完全初始化.由于`someProperty`不需要初始化而你正在调用依赖于`self`的方法,所以Swift离开初始化上下文并且`didSet`将运行. (49认同)
  • @Mark认真,试图将常识或逻辑应用于Swift是荒谬的.但你可以相信赞成或自己尝试.我刚刚做了,它的工作原理.是的,根本没有任何意义. (4认同)
  • 为什么这与init中的self.someProperty = newValue不同?你有一个工作测试案例吗? (3认同)
  • 看起来像在实际的init()之外设置的任何东西都会调用didSet.字面上任何未设置`init(){*HERE*}`的东西都会调用didSet.非常适合这个问题,但使用辅助函数设置一些值会导致调用didSet.如果要重用该setter函数,那就太好了. (3认同)
  • 不知道它为什么有效 - 但确实如此.直接在init()中设置新值 - Method不调用didSet() - 但是使用给定的方法setSomeProperty()可以. (2认同)

Cha*_*ism 74

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

class Classy {

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

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

}
Run Code Online (Sandbox Code Playgroud)

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

  • 这很聪明,不会用冗余方法污染类. (2认同)
  • 聪明的答案!注意:如果您的类是其他子类的子类,则需要在({self.foo = foo})()之前调用super.init()。 (2认同)

小智 10

我有同样的问题,这对我有用

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

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

    func doStuff() {
        // do stuff now that someProperty is set
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这种方法不再有效。编译器会引发两个错误:–在初始化所有存储的属性之前,在方法调用'$ defer'中使用的'self'–从初始化程序返回而未初始化所有存储的属性 (2认同)