解决Swift属性覆盖中的递归 [英] Resolve recursion in Swift property override
问题描述
这是Swift中针对UILabel的一个简单扩展,
Here's a simple extension in Swift, for UILabel,
let dur=0.1 // (set to say 2.0 to see the effect more clearly)
extension UILabel
{
func change(s:String)->()
{
print("attempting 'change' with \(s)")
UIView.animateWithDuration( dur,
animations: { self.alpha = 0.2 },
completion:
{ _ in
self.text = s ///CCC
UIView.animateWithDuration( dur,
animations: { self.alpha = 1.0 })
})
}
}
使用UILabel,只需执行此操作
with a UILabel, simply do this
aLabel.change("hello there")
它将快速从旧文本过渡到新文本.没问题.
It will quickly blend from the old to the new text. No problem.
当然,如果我们可以编写此代码会更好...
Of course, it would be better if we could write this ...
aLabel.text = "hello there"
要做到这一点,只需使用新版本的".text"属性创建一个新的UILabel类.
To do that, just make a new UILabel class, with a new version of the ".text" property.
class CoolLabel:UILabel
{
override var text:String?
{
get { return super.text }
set { super.change(newValue!) } //PROBLEM! AAA
}
}
但是!它行不通:它陷入了无休止的循环.
But! It doesn't work: it goes in to an endless loop.
请注意change()扩展名中的"self.text":此时进入循环.
Notice the "self.text" in the change() extension: at that point it goes in to a loop.
(我也尝试过set { self.change(newValue!) }
,它不起作用.)
(I also tried set { self.change(newValue!) }
and it does not work.)
以下内容可以正常工作:
The following works perfectly:
class TOLabel:UILabel
{
override var text:String?
{
get { return super.text }
set
{
UIView.animateWithDuration( dur,
animations: { self.alpha = 0.2 },
completion:
{ _ in
super.text = newValue //BBB
UIView.animateWithDuration( dur,
animations: { self.alpha = 1.0 })
})
}
}
}
那很好,但是我在第一个版本中做错了什么?
That's fine, but what am I doing wrong in the first version?
您如何编写setter才能成功使用.change扩展名?
How would you write the setter to successfully use the .change extension?
顺便说一句,对于任何在这里阅读的人来说,都是如何更全面地继承IBLabel的子类,您必须重写指定的初始化程序,并且,您需要保留本地的实时"文本版本,以便getter正确答复.在动画过程中,在设置文字之后.
By the way, for anyone reading here is how you would more fully subclass IBLabel, you have to override the designated initialiser, and, you'll need to keep a local "realtime" version of text so that the getter replies correctly during animations, immediately after you set the text.
class TOLabel:UILabel
{
private var _text:String?
required init?(coder aDecoder: NSCoder)
{
super.init(coder: aDecoder)
self._text = super.text;
}
override var text:String?
{
get { return self._text }
set {
self._text = newValue;
UIView.animateWithDuration( dur,
animations: { self.alpha = 0.2 },
completion:
{ _ in
super.text = self._text
UIView.animateWithDuration( dur,
animations: { self.alpha = 1.0 })
})
}
}
}
推荐答案
使用self.text=
在扩展方法change
中分配新值时将调用您的递归,因为这将调用setter,后者将调用
Your recursion is called when you use self.text=
to assign a new value in your change
extension method because this will call the setter, which calls change
and so on.
在第二个示例中,避免了递归,因为可以从子类设置器中调用超类设置器.您的扩展名中没有此选项,因为您的代码正在作为UILabel
的扩展名运行,因此没有要调用的超类设置器.
In your second example, you avoid recursion because you can call the superclass setter from your subclass setter. You don't have this option in your extension because your code is running as an extension to UILabel
and therefore there is no superclass setter to call.
在这种情况下,可以说创建子类而不是使用扩展是更正确的方法
Arguably creating a subclass rather than using extension is the more correct approach in this instance anyway
这篇关于解决Swift属性覆盖中的递归的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!