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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/25230780/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 01:47:12  来源:igfitidea点击:

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

iosswiftdidset

提问by Logan

Question

Apple's docsspecify that:

Apple 的文档规定:

willSet and didSet observers are not called when a property is first initialized. They are only called when the property's value is set outside of an initialization context.

willSet 和 didSet 观察者在属性第一次初始化时不会被调用。只有当属性的值设置在初始化上下文之外时才会调用它们。

Is it possible to force these to be called during initialization?

是否可以在初始化期间强制调用这些?

Why?

为什么?

Let's say I have this class

假设我有这门课

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

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

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

I created the method doStuff, to make the processing calls more concise, but I'd rather just process the property within the didSetfunction. Is there a way to force this to call during initialization?

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

Update

更新

I decided to just remove the convenience intializer for my class and force you to set the property after initialization. This allows me to know didSetwill always be called. I haven't decided if this is better overall, but it suits my situation well.

我决定只删除我的类的便利初始化器,并强制您在初始化后设置属性。这让我知道didSet总会被调用。我还没有决定这是否总体上更好,但它很适合我的情况。

采纳答案by Oliver

Create an own set-Method and use it within your init-Method:

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

By declaring somePropertyas type: AnyObject!(an implicitly unwrapped optional), you allow self to fully initialize without somePropertybeing set. When you call setSomeProperty(someProperty)you're calling an equivalent of self.setSomeProperty(someProperty). Normally you wouldn't be able to do this because self hasn't been fully initialized. Since somePropertydoesn't require initialization and you are calling a method dependent on self, Swift leaves the initialization context and didSet will run.

通过声明someProperty为 type:(AnyObject!一个隐式解包的可选),你允许 self 完全初始化而不 someProperty被设置。当您致电时, setSomeProperty(someProperty)您正在致电相当于 self.setSomeProperty(someProperty). 通常你不能这样做,因为 self 还没有完全初始化。由于 someProperty不需要初始化并且您正在调用依赖于 self 的方法,因此 Swift 离开初始化上下文并且 didSet 将运行。

回答by Brian Westphal

If you use deferinside of an initializer, for updating any optional properties or further updating non-optional properties that you've already initialized and after you've called any super.init()methods, then your willSet, didSet, etc. will be called. I find this to be more convenient than implementing separate methods that you have to keep track of calling in the right places.

如果您使用defer的内部初始化,更新任何可选属性或进一步更新不可选的属性,你已经初始化,之后你调用的任何super.init()方法,那么你的willSetdidSet等会被调用。我发现这比实现单独的方法更方便,您必须在正确的位置跟踪调用。

For example:

例如:

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

Will yield:

将产生:

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

回答by original_username

As a variation of Oliver's answer, you could wrap the lines in a closure. Eg:

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

class Classy {

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

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

}

Edit: Brian Westphal's answer is nicer imho. The nice thing about his is that it hints at the intent.

编辑:Brian Westphal 的回答更好,恕我直言。他的好处在于它暗示了意图。

回答by Carmine Cuofano

I had the same problem and this works for me

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

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

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

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

回答by onmyway133

This works if you do this in a subclass

如果您在子类中执行此操作,则此方法有效

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()

In aexample, didSetis not triggered. But in bexample, didSetis triggered, because it is in the subclass. It has to do something with what initialization contextreally means, in this case the superclassdid care about that

a例子中,didSet不会被触发。但是在bexample 中,didSet是触发的,因为它在子类中。它必须做一些initialization context真正意味着什么,在这种情况下,superclass确实关心那个

回答by C?ur

In the particular case where you want to invoke willSetor didSetinside initfor a property available in your superclass, you can simply assign your super property directly:

在您想要为超类中可用的属性调用willSetdidSetinside的特定情况下init,您可以简单地直接分配您的超属性:

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

Note that Charlesismsolution with a closure would always work too in that case. So my solution is just an alternative.

请注意,在这种情况下,带有闭包的Charlesism解决方案也始终有效。所以我的解决方案只是一个替代方案。

回答by Snowman

While this isn't a solution, an alternative way of going about it would be using a class constructor:

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

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

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

回答by yshilov

You can solve it in obj-с way:

你可以用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
    }
}