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
。我还没有决定这总体上是否更好,但它很适合我的情况。
答案 0 :(得分:240)
如果您在初始化程序中使用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
答案 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
内调用didSet
或init
来获取超类中可用的属性,您可以直接分配您的超级属性:
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
}
}