【问题标题】:There is difference in behaviour when an instance variable is computed instead of being stored当计算实例变量而不是存储实例变量时,行为会有所不同
【发布时间】:2019-06-01 11:40:16
【问题描述】:

我有一个从xib 文件启动的自定义UIView 类。它具有名为title 的实例属性,类型为String?。只要设置了title 属性,UITextField 的文本就会更改为title 属性的值。

如果title 属性是存储属性,则程序按预期工作。

如果title 属性是计算属性,那么程序会因EXC_BAD_ACCESS 错误而崩溃,我认为这是因为IBOutlet 尚未初始化。

谁能解释为什么如果title 是一个存储属性,它可以工作,但如果它是一个计算属性,它会失败?

以下是源代码- NibViewUIView 的子类,负责处理 xib 文件的加载

class NibView: UIView {
    override init(frame: CGRect) {
        super.init(frame: frame)
        loadNib()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        loadNib()
    }
}

loadNib 方法的实现在扩展中

extension UIView {
    func loadNib() {
        guard let view = nib.instantiate(withOwner: self, options: nil).first as? UIView else { return }

        view.frame = bounds
        addSubview(view)
    }
}

UIView 上的nib 属性的定义在另一个扩展中

extension UIView {
    static var nib: UINib {
        return UINib(nibName: String(describing: self), bundle: nil)
    }

    var nib: UINib {
        return type(of: self).nib
    }
}

下面的类是具有title属性的类。

class ProgressView: NibView {
    var title: String? {
        didSet {
            titleLabel.text = title
        }
    }

    @IBOutlet private weak var titleLabel: UILabel!
}

上面的类使用如下-

let view = ProgressView()
addSubview(view)
view.title = "Loading"

运行上述代码按预期工作。 但是,如果 ProgressView 的实现被更改为使用如下计算属性,那么它会失败

class ProgressView: NibView {
    var title: String? {
        get {
            return titleLabel.text
        }
        set {
            titleLabel.text = newValue
        }
    }

    @IBOutlet private weak var titleLabel: UILabel!
}

谁能指出为什么当 title 属性被计算而不是被存储时,行为的差异在哪里?

编辑 - 主线程因“线程 1:EXC_BAD_ACCESS (code=EXC_I386_GPFLT)”而崩溃

调用堆栈顶部的方法是“ProgressView.title.modify”。

编辑 2- 我不确定我做了什么,但在重新启动 xcode 后我无法重现该问题。即使使用了计算属性,它也能按预期工作。

【问题讨论】:

  • 如果您将get 更改为return titleLabel?.text ?? "" 会发生什么
  • 只要return titleLabel?.text 就足够了,因为titleString?
  • “我认为这是因为” - 在调试器中运行您的代码,看看实际发生了什么,而不是做出假设。用您发现的内容更新您的问题。
  • 仅供参考 - 当您使用存储属性时,getter 不会做任何事情。 getter 可能无法反映标签中的实际值。这与问题无关,但存储属性版本和计算值版本之间存在很大差异。
  • 您应该在创建 ProgressView 的所有位置以及获取和设置 Computed 属性的所有行中添加断点。我会在 Xcode 中做,但我现在在我的 iPhone 上。关于究竟是什么导致了这个错误,这将是非常有用的。如果您在控制台/调试器中包含打印的错误名称,那也很棒。如果在我给电脑充电时这个问题还没有得到解答,我会试着弄清楚。

标签: ios swift getter-setter iboutlet


【解决方案1】:

您的描述远非解释性的,但我猜有一个 ProgressView 笔尖,其中文件的所有者是 ProgressView,并且文件所有者有一个 titleLabel 出口到笔尖内的标签。 (我假设这是因为否则我无法解释您对withOwner: self 的使用。)

基于这个假设,我无法重现任何问题。你表达title 的两种方式对我来说都很好。我放了print 语句以确保调用了正确的语句,并且确实如此;无论这是 didSet 还是计算属性的设置器,我们都可以正常加载,我会看到“正在加载”文本。

我的代码在视图控制器的 viewDidLoad 中,如果这有影响的话。

(顺便说一句,我怀疑您对ProgressView() 的使用。这会导致视图大小为零。这似乎没有任何区别,但这是一个坏主意。标签是零尺寸视图。如果零尺寸视图剪裁了其子视图,则标签将不可见。即使零尺寸视图不剪裁其子视图,如果标签是按钮,该按钮也不起作用。零尺寸视图是个坏主意。你应该给你的 ProgressView 一个真实的框架。)

【讨论】:

  • 如果这没有帮助,请制作一个最小的完整可验证示例项目并将其发布,以便我可以看到您的设置与我的不同之处。
  • 您对我设置自定义 UIView 的方式是正确的。自定义 UIView 的代码改编自 medium.com/swift2go/… 。我无法重现我在问题中提到的问题。
  • 如果你不能重现你自己的问题,那么事情就结束了,不是吗?
  • 是的。事情已经结束了。
  • 好吧,我为此花费了很多时间和精力,所以您可能想接受我的回答。如果没有,您可以删除问题。
猜你喜欢
  • 1970-01-01
  • 2020-11-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-28
  • 2016-05-27
  • 2012-05-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多