【问题标题】:Adding weak Subview is causing crash for iOS14添加弱子视图导致iOS14崩溃
【发布时间】:2020-08-04 10:47:15
【问题描述】:

我收到了 iOS14 用户的崩溃日志。我可以做些什么吗,还是应该等到 iOS14 正式发布?

这是带有 textField 的 myCustomTableViewCell:

class TextTVCell: UITableViewCell, UITextFieldDelegate {
weak var delegate: TextTVCellDelegate?

weak var textField: UITextField? = {         // I have to make it optional because of weak reference
    let textField = UITextField()
    textField.translatesAutoresizingMaskIntoConstraints = false
    return textField
}()

override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
    super.init(style: style, reuseIdentifier: reuseIdentifier)
    setupSubviews()
}
required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
}

private func setupSubviews() {
    contentView.addSubview(textField!)
    ......... // constraints, ...
} 

强制解包会导致任何 iOS14 设备崩溃。下面它工作得很好。

这是我的崩溃日志:

Incident Identifier: AB559313-AD92-4B3A-AA90-97AE47315DEF
Hardware Model:      iPhone9,4
Process:             FormaleBriefe [6654]
Path:                /private/var/containers/Bundle/Application/798B5135-DEB8-451D-858F-AE9FA47F6C7B/FormaleBriefe.app/FormaleBriefe
Identifier:          frugalResolution.Briefe
Version:             5 (2.2.2)
AppStoreTools:       11E707
AppVariant:          1:iPhone9,4:13
Code Type:           ARM-64 (Native)
Role:                Foreground
Parent Process:      launchd [1]
Coalition:           frugalResolution.Briefe [1134]


Date/Time:           2020-08-02 22:52:50.5708 +0200
Launch Time:         2020-08-02 22:52:48.3752 +0200
OS Version:          iPhone OS 14.0 (18A5332f)
Release Type:        Beta
Baseband Version:    4.50.04
Report Version:      104

Exception Type:  EXC_BREAKPOINT (SIGTRAP)
Exception Codes: 0x0000000000000001, 0x0000000104fac2ac
Termination Signal: Trace/BPT trap: 5
Termination Reason: Namespace SIGNAL, Code 0x5
Terminating Process: exc handler [6654]
Triggered by Thread:  0

Thread 0 name:
Thread 0 Crashed:
0   FormaleBriefe                   0x0000000104fac2ac TextTVCell.setupSubviews() + 3800 (TextTVCell.swift:41)
1   FormaleBriefe                   0x0000000104fab458 TextTVCell.setupSubviews() + 132 (TextTVCell.swift:0)
2   FormaleBriefe                   0x0000000104facb98 specialized TextTVCell.init(style:reuseIdentifier:) + 296 (TextTVCell.swift:32)
3   FormaleBriefe                   0x0000000104fab374 @objc TextTVCell.init(style:reuseIdentifier:) + 72 (<compiler-generated>:0)
4   UIKitCore                       0x000000019b63f448 -[UITableView _dequeueReusableViewOfType:withIdentifier:] + 532 (UITableView.m:8843)
5   FormaleBriefe                   0x0000000104f8ca34 LetterWriterVC.tableView(_:cellForRowAt:) + 1504 (LetterWriterVC.swift:555)
6   FormaleBriefe                   0x0000000104f8d6bc @objc

【问题讨论】:

  • 我很惊讶这曾经奏效。试图猜测它为什么会这样真的没有意义。你的weak 总是错的。删除它。
  • 您绝对不要等到 iOS 14 正式发布后才修复 iOS 14 错误,这正是开发人员测试版的目的。在您的计算机上安装 Xcode 测试版并在您的一台设备上安装测试版,或者使用模拟器,然后开始修复。
  • 这是我去年刚开始编程时的代码。谢谢你现在说清楚。

标签: swift weak-references ios14 swift-optionals


【解决方案1】:

您的代码始终是反模式(也就是错误的)。一个简单的测试将显示模式的问题。我将使用视图控制器:

weak var textField: UITextField? = { 
    let textField = UITextField()
    return textField
}()

override func viewDidLoad() {
    super.viewDidLoad()
    let tf = self.textField
    print(tf as Any) // nil
}

我们运行应用程序,控制台打印nil。你可以很容易地看到原因。在某些时候,textField 实例属性被初始化,因此闭包运行并返回一个文本字段并分配给textField。但是那个参考是weak!这意味着:不要抓住我。所以实例属性保留文本字段;它删除它,文本字段不存在,引用被 nil 替换。

简单的解决方案:不要那样做!正如我所说,这是一种反模式。删除单词weak,或者,如果您打算保持对文本字段的弱引用,请使用不同的模式,例如:

weak var textField: UITextField?
private func createTextField() -> UITextField  {
    let textField = UITextField()
    self.textField = textField
    return textField
}
override func viewDidLoad() {
    super.viewDidLoad()
    print(tf as Any) // text field
    print(self.textField as Any) // text field
    // and now put it into the interface immediately
    // or it will go out of existence _again!_
}

就我个人而言,我所做的并非如此。我这样做:

weak var textField: UITextField?
private func createTextField() -> UITextField  {
    let textField = UITextField()
    return textField
}
override func viewDidLoad() {
    super.viewDidLoad()
    let tf = self.createTextField()
    self.textField = tf
    // and now put it into the interface immediately or it will go out of existence
}

在现实生活中,该代码将位于 setup 例程中。换句话说,我将其作为setup 例程的工作来制作视图并将它们分配给它们的弱引用,同时将它们放入接口中。

当然,还有另一种可能性,就是放弃weak,不做任何其他事情。让textField 成为强有力的参考!对子视图进行强引用绝对没有错——当然,除非该子视图也对您有强引用,但这种情况不太可能发生,如果出现,可以避免。

【讨论】:

  • 感谢您的解释!我真的很喜欢最后一种方法!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多