提问人:Logan 提问时间:8/11/2014 最后编辑:Slipp D. ThompsonLogan 更新时间:7/7/2020 访问量:66294
是否可以允许在 Swift 初始化期间调用 didSet?
Is it possible to allow didSet to be called during initialization in Swift?
问:
问题
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
}
}
我创建了 方法 ,以使处理调用更简洁,但我宁愿只处理函数中的属性。有没有办法在初始化期间强制调用它?doStuff
didSet
更新
我决定只删除我的类的便利初始化程序,并强制您在初始化后设置属性。这让我知道将永远被召唤。我还没有决定这是否总体上更好,但它很适合我的情况。didSet
答:
创建一个自己的 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
}
}
通过声明为 type: (隐式 unwrapped 可选),您允许 self 在不设置的情况下完全初始化。当您调用时,您调用的等效于 .通常你无法做到 这样做是因为 self 尚未完全初始化。由于不需要初始化,并且您正在调用 方法依赖于 self,Swift 离开初始化上下文和 didSet 将运行。
someProperty
AnyObject!
someProperty
setSomeProperty(someProperty)
self.setSomeProperty(someProperty)
someProperty
评论
someProperty
AnyObject!
self
someProperty
setSomeProperty(someProperty)
self.setSomeProperty(someProperty)
self
someProperty
self
didSet
init() { *HERE* }
作为 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
}
}
评论
self.someProperty
var someProperty: AnyObject! = nil { didSet { ... } }
如果在初始值设定项内部使用,用于更新任何可选属性或进一步更新已初始化的非可选属性,并且在调用任何方法后,将调用 、 等。我发现这比实现单独的方法更方便,您必须在正确的地方跟踪调用。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
评论
defer
虽然这不是一个解决方案,但另一种方法是使用类构造函数:
class SomeClass {
var someProperty: AnyObject {
didSet {
// do stuff
}
}
class func createInstance(someProperty: AnyObject) -> SomeClass {
let instance = SomeClass()
instance.someProperty = someProperty
return instance
}
}
评论
someProperty
如果您在子类中执行此操作,则此操作有效
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
评论
在特定情况下,如果要调用超类中可用的属性或内部,则可以直接分配超级属性:willSet
didSet
init
override init(frame: CGRect) {
super.init(frame: frame)
// this will call `willSet` and `didSet`
someProperty = super.someProperty
}
请注意,在这种情况下,带有闭包的查尔斯主义解决方案也始终有效。所以我的解决方案只是一个替代方案。
我遇到了同样的问题,这对我有用
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
}
didSet 应该在调用 super.init() 之后调用。
有一件事我没有尝试过,但也可能有效:
init(test: int) {
defer { self.test = test }
}
注意:您需要使属性为 null,或为它们设置默认值,或解开类属性的包装。
评论
defer
convenience init(someProperty: AnyObject) { self.init() defer { self.someProperty = someProperty }