当计算实例变量而不是存储实例变量时,行为会有所不同

There is difference in behaviour when an instance variable is computed instead of being stored

我有一个从 xib 文件启动的自定义 UIView class。它有实例 属性,名为 title,类型为 String?。每当设置 title 属性 时,UITextField 的文本将更改为 title 属性.

的值

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

如果 title 属性 是计算的 属性,则程序崩溃并出现 EXC_BAD_ACCESS 错误,我认为这是因为 IBOutlet 没有尚未初始化。

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

以下是源代码- NibViewUIView 的子class 并处理 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)
    }
}

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

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

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

下面的class是标题为属性的class。

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

    @IBOutlet private weak var titleLabel: UILabel!
}

上面的class用法如下-

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 属性 而不是存储时的行为差异在哪里?

编辑 - 主线程崩溃 "Thread 1: EXC_BAD_ACCESS (code=EXC_I386_GPFLT)"

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

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

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

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

我的代码在视图控制器的 viewDidLoad 中,如果有区别的话。

(顺便说一下,我怀疑你对 ProgressView() 的使用。这导致了 zero-size 视图。它可能看起来没有任何区别,但这是一个坏主意。标签是 zero-size 视图的子视图。如果 zero-size 视图剪裁了它的子视图,标签将不可见。即使 zero-size 视图不剪裁它的子视图,如果标签是一个按钮,按钮将不起作用。Zero-size 视图是个坏主意。你应该给你的 ProgressView 一个真实的框架。)