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

问题

苹果公司的文档指定:

当属性第一次初始化时,不会调用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总是会被调用。我还没有决定这是否是更好的整体,但它很适合我的情况。

61230 次浏览

创建一个自己的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声明为类型 Unwrapped可选),你允许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的回答更好。这句话的好处是暗示了他的意图。

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

如果你在初始化器中使用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

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

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


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

如果你在子类中这样做,这是可行的

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确实关心这一点

在这种特殊情况下,你想在init中调用willSetdidSet来获取父类中可用的属性,你可以直接赋值父属性:

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

注意,带有闭包的Charlesism解决方案在这种情况下也总是有效。所以我的解只是一种选择。

我也有同样的问题,这个方法对我很有效

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


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


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

不幸的是,根类初始化时不会调用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
}

在super.init()被调用之后,didSet应该被调用。

有一件事我没有尝试过,但可能也有用:

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

注意:你将需要使你的属性为空,或为它们设置一个默认值,或展开类属性。