I have a custom UIView
class that is initiated from the xib
file. It has instance property called title
of type String?
. Whenever, the title
property is set, the text of a UITextField
gets changed to the value of the title
property.
If the title
property is a stored property, the program works as expected.
If the title
property is a computed property, then the program crashes with EXC_BAD_ACCESS
error which I assume is because an IBOutlet
had not yet been initialized.
Can anyone explain why if title
is a stored property, it works but if it is an computed property it fails?
Following is the source code-
The NibView
is a subclass of UIView
and handles the loading of xib file
class NibView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
loadNib()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
loadNib()
}
}
The implementation of loadNib
method is inside an extension
extension UIView {
func loadNib() {
guard let view = nib.instantiate(withOwner: self, options: nil).first as? UIView else { return }
view.frame = bounds
addSubview(view)
}
}
The definition of nib
property on UIView
is in another extension
extension UIView {
static var nib: UINib {
return UINib(nibName: String(describing: self), bundle: nil)
}
var nib: UINib {
return type(of: self).nib
}
}
The following class is the class which has the title property.
class ProgressView: NibView {
var title: String? {
didSet {
titleLabel.text = title
}
}
@IBOutlet private weak var titleLabel: UILabel!
}
The above class is used as follows-
let view = ProgressView()
addSubview(view)
view.title = "Loading"
Running the above code works as expected.
However if the implementation of ProgressView
is changed to use a computed property as below, then it fails
class ProgressView: NibView {
var title: String? {
get {
return titleLabel.text
}
set {
titleLabel.text = newValue
}
}
@IBOutlet private weak var titleLabel: UILabel!
}
Can anyone point out why where is difference in behaviour when the title
property is computed instead of being stored?
Edit - The main thread crashes with "Thread 1: EXC_BAD_ACCESS (code=EXC_I386_GPFLT)"
The method on top of the call stack is "ProgressView.title.modify".
Edit 2- I am not sure what I have done but I am unable to reproduce the issue after restarting xcode. Even if computed property is used, it works as expected.
Your description is far from explanatory, but I'm guessing that there is a ProgressView nib in which the File's Owner is a ProgressView, and there is an titleLabel
outlet from the File's owner to a label inside the nib. (I assume this because otherwise I can't explain your use of withOwner: self
.)
On that assumption I can't reproduce any problem. Both your ways of expressing title
work just fine for me. I put print
statements to make sure the right one was being called, and it is; no matter whether this is a didSet
or the setter of a computed property, we load just fine and I see the "Loading" text.
My code is in a view controller's viewDidLoad
, if that makes a difference.
(By the way, I regard your use of ProgressView()
with suspicion. This results in a zero-size view. It might not seem to make any difference, but it's a bad idea. The label is a subview of the zero-size view. If the zero-size view clipped its subviews, the label would be invisible. Even if the zero-size view does not clip its subviews, if the label were a button, the button would not work. Zero-size views are a bad idea. You should give your ProgressView a real frame.)