iOS Swift子类化UIStackView时使用weak属性引发崩溃的原因
兄弟,你遇到的崩溃完全是对weak修饰符的理解偏差导致的,咱们一步步拆解:
当你把container和titleLabel改成weak后,执行container = UIView()这行代码时,新创建的UIView实例没有任何强引用持有它——weak变量本身不会增加对象的引用计数,所以这行代码执行完的瞬间,这个UIView就被ARC(自动引用计数)立即回收了,container也就变成了nil。接下来你再去访问container.backgroundColor,自然就触发了“解包nil可选值”的致命错误。
为什么原来的非Weak版本没问题?
原来的代码里,container是强引用变量,它会持有新创建的UIView实例,直到你执行addArrangedSubview(container)——这时候UIStackView的arrangedSubviews数组会对这个container视图持有强引用。也就是说,此时有两个强引用指向container:一个是你的自定义StackView的container属性,另一个是StackView内部的数组。这样container的生命周期就和StackView绑定了,不会提前释放。
关于你担心的引用循环——其实完全没必要
你怕自定义StackView和子视图形成循环引用?放心,iOS视图层级的引用关系是父视图强引用子视图,子视图弱引用父视图:
- 你的自定义StackView(父)强引用container(子)
- container的
superview属性是weak的,指向你的StackView - 同理,container强引用titleLabel,titleLabel的
superview是weak指向container
这种结构根本不会形成循环引用,所以你完全不需要给这些子视图属性加weak修饰符。
正确的做法
直接把weak修饰符去掉,保持原来的强引用即可:
class UIButtonHeaderView: UIStackView { // MARK: - Properties var container: UIView! var titleLabel: UILabel! // MARK: - Initialization private func setUp() { // Set container. container = UIView() container.backgroundColor = .blue addArrangedSubview(container) // Set label. titleLabel = UILabel() container.addSubview(titleLabel) titleLabel.translatesAutoresizingMaskIntoConstraints = false titleLabel.leadingAnchor.constraint(equalTo: container.leadingAnchor).isActive = true titleLabel.bottomAnchor.constraint(equalTo: container.bottomAnchor).isActive = true titleLabel.trailingAnchor.constraint(lessThanOrEqualTo: container.trailingAnchor).isActive = true titleLabel.widthAnchor.constraint(greaterThanOrEqualToConstant: 0).isActive = true titleLabel.heightAnchor.constraint(greaterThanOrEqualToConstant: 0).isActive = true titleLabel.text = "This is just a simple test!!" titleLabel.backgroundColor = .green titleLabel.textColor = .red titleLabel.sizeToFit() } override init(frame: CGRect) { super.init(frame: frame) setUp() } required init(coder: NSCoder) { super.init(coder: coder) setUp() } }
这样修改后,container和titleLabel的生命周期会和你的自定义StackView保持一致,既不会提前释放导致崩溃,也不会产生循环引用的内存问题。
内容的提问来源于stack exchange,提问作者Nestarneal

