Добавление слабого Subview вызывает ошибку sh для iOS14 - PullRequest
0 голосов
/ 04 августа 2020

Я получаю cra sh логи для пользователей с iOS14. Могу ли я что-нибудь с этим сделать или подождать, пока iOS14 официально не появится?

Вот 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, ...
} 

Принудительное развертывание вызывает ошибку sh на любом устройстве с iOS14. Ниже все работает нормально.

Вот мой cra sh log:

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

1 Ответ

2 голосов
/ 04 августа 2020

Ваш код всегда был антипаттерном (то есть неправильным). Простой тест покажет проблему с шаблоном. Я буду использовать контроллер представления:

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 сильной ссылкой! Нет абсолютно ничего плохого в наличии сильной ссылки на подпредставление - если, конечно, это подпредставление также не имеет сильной ссылки на вас, но это маловероятно и может быть предотвращено в случае возникновения.

...