【问题标题】:Resolve recursion in Swift property override解决 Swift 属性覆盖中的递归
【发布时间】:2015-09-20 22:45:49
【问题描述】:

这是一个简单的 Swift 扩展,用于 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,只需这样做

aLabel.change("hello there")

它将迅速从旧文本融合到新文本。没问题。

当然,如果我们能写这个就更好了……

aLabel.text = "hello there"

为此,只需创建一个新的 UILabel 类,并使用新版本的“.text”属性。

class CoolLabel:UILabel
    {
    override var text:String?
        {
        get { return super.text }
        set { super.change(newValue!) } //PROBLEM! AAA
        }
    }

但是!它不起作用:它进入一个无限循环。

注意 change() 扩展中的“self.text”:此时它进入一个循环。

(我也试过set { self.change(newValue!) },还是不行。)

以下工作完美:

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

这很好,但我在第一个版本中做错了什么?

您将如何编写设置器以成功使用 .change 扩展名?


顺便说一句,对于阅读此处的任何人来说,您将如何更充分地继承 IBLabel,您必须覆盖指定的初始化程序,并且您需要保留本地“实时”版本的文本,以便 getter 正确回复在动画期间,在您设置文本之后立即进行。

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

【问题讨论】:

  • 这是您需要绕过 setter 的实例之一。您需要在更改方法中使用super.text,就像在上一个代码示例中一样。
  • 但事情是这样的。在我的评论//PROBLEM 中,我确实将 SUPER 发送到“改变”。但是当changechange的第6行运行时,它是在TOLabel上运行的,而不是在super... :O
  • 对。您遇到的问题是您无法绕过setter,因此无法避免递归。老实说,无论如何,子类可能是更好的方法。
  • 嗯,我相信你,但我不明白为什么我可以在 BBB 上说“super.text=”,但我不能在 CCC 上说 super.text,在 AAA 上拨打 change .
  • 因为当您在子类的上下文中执行代码时,super.text 不会调用您的子类设置器。当您执行change 时,它会调用self.text 的设置器,这是子类设置器,这会导致递归。如果你在你的子类(它可能属于它)中实现更改,你可以使用super.text,你不会有问题

标签: ios swift properties


【解决方案1】:

当您使用self.text= 在您的change 扩展方法中分配一个新值时调用您的递归,因为这将调用setter,它调用change 等等。

在您的第二个示例中,您避免了递归,因为您可以从子类设置器中调用超类设置器。您的扩展中没有此选项因为您的代码作为UILabel 的扩展运行,因此没有要调用的超类设置器

无论如何,在这种情况下,创建子类而不是使用扩展是更正确的方法

【讨论】:

    猜你喜欢
    • 2018-06-09
    • 1970-01-01
    • 1970-01-01
    • 2014-12-28
    • 2012-08-15
    • 1970-01-01
    • 1970-01-01
    • 2015-02-20
    • 1970-01-01
    相关资源
    最近更新 更多