Xcode 9.3.1 - iOS Swift 4.1 UITextView.becomeFirstResponder Поток 1: EXC_BAD_ACCESS (код = 1, адрес = 0x4d555458) - PullRequest
0 голосов
/ 17 мая 2018

После обновления до Xcode 9.3.1 я столкнулся с падением Thread BAD_ACCESS, когда клавиатура появляется, когда UITextView становится ручным средством становленияFirstResponder.

Для ясности, раньше это работало как раз перед обновлением Xcode до 9.3.1, но теперь я не могу понять, почему происходит сбой.

Я сделал урезанный пример, чтобы продемонстрировать это. В предоставленном мною UIViewController нет других переменных или функций.

class ErrorTestsController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        setupUIElements()
    }

    private func setupUIElements() {
        view.backgroundColor = UIColor.red
        view.addSubview(blankTextView)

        blankTextView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        blankTextView.widthAnchor.constraint(equalToConstant: 200.0).isActive = true
        blankTextView.heightAnchor.constraint(equalToConstant: 40.0).isActive = true

        blankTextViewBottomAnchor = blankTextView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: 0.0)
        blankTextViewBottomAnchor?.isActive = true
        // Whether I use safeAreaLayoutGuide or the regular view's bottomAnchor changes nothing

        // Add observers to move the blankTextView up when the keyboard appears
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(notification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)

        // add a UITapGestureRecognizer to make the blankTextView becomeFirstResponder and show the keyboard (will also trigger the above NotificationObserver)
        view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(showAddTextView)))
    }

    // Define reference to bottomAnchor of the UITextView called "blankTextView" (using iOS 9.0 + NSLayoutConstraints)
    public var blankTextViewBottomAnchor: NSLayoutConstraint?

    public var blankTextView: UITextView = {
        let textview = UITextView(frame: .zero, textContainer: nil)
        textview.translatesAutoresizingMaskIntoConstraints = false
        textview.backgroundColor = UIColor.black
        return textview
    }()

    @objc private func showAddTextView() {
        blankTextView.becomeFirstResponder()
    }

    @objc private func keyboardWillShow(notification: NSNotification) {
        guard
            let keyboardFrame = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as AnyObject).cgRectValue,
            let keyboardDuration = (notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey] as AnyObject).doubleValue
            else { return }

        blankTextViewBottomAnchor?.constant = -keyboardFrame.height

        UIView.animate(withDuration: keyboardDuration) {
            self.view.layoutIfNeeded()
        }
    }

    @objc private func keyboardWillHide(notification: NSNotification) {
        guard
            let keyboardDuration = (notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey] as AnyObject).doubleValue
            else { return }

        blankTextViewBottomAnchor?.constant = 0.0

        UIView.animate(withDuration: keyboardDuration) { 
            self.view.layoutIfNeeded()
        }
    }
}

Вот вывод сбоя:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFString isHidden]: unrecognized selector sent to instance 0x1d0224560'
*** First throw call stack:
(0x181c76d8c 0x180e305ec 0x181c84098 0x181c7c5c8 0x181b6241c 0x18b8351bc 0x18bdb588c 0x18b9c1310 0x18b9c132c 0x18b9c132c 0x18b9c0eec 0x18b9c0850 0x18b9b9934 0x18b84f30c 0x18b9b0330 0x18b8f1b30 0x18b8f1f4c 0x18bac6304 0x100979324 0x100979368 0x18ba26750 0x18bf932a4 0x18bb88e6c 0x18ba257a8 0x18bf84ac4 0x18ba1f540 0x18ba1f078 0x18ba1e8dc 0x18ba1d238 0x18c1fec0c 0x18c2011b8 0x18c201518 0x18c1fa258 0x181c1f404 0x181c1ec2c 0x181c1c79c 0x181b3cda8 0x183b1f020 0x18bb1d78c 0x10089e748 0x1815cdfc0)
libc++abi.dylib: terminating with uncaught exception of type NSException

Этот сбой происходит, независимо от того, использую ли я UITapGestureRecognizer в self.view или вручную нажимаю на UITextView. Оба приводят к одному и тому же сбою.

ПРИМЕЧАНИЕ. В некоторых случаях я наблюдал, что фактический результат сбоя отличается. В этом случае он отображает «[__NSCFString isHidden]:». В других случаях вывод был "[__NSArrayI position]:". Однако я никогда не менял код.

Но это всегда какая-то форма EXC_BAD_ACCESS.

РЕДАКТИРОВАТЬ: ПОЛНАЯ СТАДИЯ TRACE

2018-05-17 12:41:23.714232-0400 TESTAPP[4368:1363924] [Application] Failed to instantiate the default view controller for UIMainStoryboardFile 'Main' - perhaps the designated entry point is not set?

2018-05-17 12:41:25.497939-0400 TESTAPP[4368:1363924] [MC] System group container for systemgroup.com.apple.configurationprofiles path is /private/var/containers/Shared/SystemGroup/systemgroup.com.apple.configurationprofiles
2018-05-17 12:41:25.500096-0400 TESTAPP[4368:1363924] [MC] Reading from public effective user settings.
2018-05-17 12:41:25.514580-0400 TESTAPP[4368:1363924] -[__NSCFString isHidden]: unrecognized selector sent to instance 0x1d023ec00
2018-05-17 12:41:25.515767-0400 TESTAPP[4368:1363924] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFString isHidden]: unrecognized selector sent to instance 0x1d023ec00'
*** First throw call stack:
(0x181c76d8c 0x180e305ec 0x181c84098 0x181c7c5c8 0x181b6241c 0x18b8351bc 0x18bdb588c 0x18b9c1310 0x18b9c132c 0x18b9c132c 0x18b9c0eec 0x18b9c0850 0x18b9b9934 0x18b84f30c 0x18b9b0330 0x18b8f1b30 0x18b8f1f4c 0x18bac6304 0x100fa58cc 0x100fa5910 0x18ba26750 0x18bf932a4 0x18bb88e6c 0x18ba257a8 0x18bf84ac4 0x18ba1f540 0x18ba1f078 0x18ba1e8dc 0x18ba1d238 0x18c1fec0c 0x18c2011b8 0x18c201518 0x18c1fa258 0x181c1f404 0x181c1ec2c 0x181c1c79c 0x181b3cda8 0x183b1f020 0x18bb1d78c 0x100ecad04 0x1815cdfc0)
libc++abi.dylib: terminating with uncaught exception of type NSException
(lldb) 

Редактировать 2: Все это делается программно. Не используя раскадровки. В моем AppDelegate я определяю UIWindow и его rootView следующим образом:

    window = UIWindow(frame: UIScreen.main.bounds)
    window?.makeKeyAndVisible()

    let navigationController = UINavigationController(rootViewController: ErrorTestsController())
    window?.rootViewController = navigationController

1 Ответ

0 голосов
/ 18 мая 2018

Я понял это ...

Я хочу начать с того, что решение НЕ входит в сферу применения вопроса, но я решил, что я должен опубликовать его в любом случае, так как оно может неосознанно влиять на другие элементы пользовательского интерфейса, которые, по-видимому, связаны с ним, тем самым непосредственно вызывая мою проблему.

Для начала в другом месте я определил расширение для UIView примерно так ...

extension UIView {

    func doSomething() {

        // Attempt to be sure that sublayers exist first...
        if (self.layer.sublayers != nil) && (self.layer.sublayers.count > 0) {

            self.layer.sublayers?.removeAll() 
            // These also cause crash
            // self.layer.sublayers?.removeFirst() 
            // self.layer.sublayers?.forEach { $0.removeFromSuperlayer() } 
            // self.layer.sublayers = nil
        }

        // I insert a CALayer at index 0 with:
        self.layer.insertSublayer(someCALayer, at: 0)
    }
}

ПОЯСНЕНИЯ:

В какой-то момент я вручную добавляю CALayer к тому, что UIView вызывает эту расширенную функцию (не self.view UIViewController, а пользовательский UIView). У меня было требование, где мне нужно было заменить этот подслой на другой CALayer, в основном, с нуля. Поэтому я очистил текущий, если он был.

Так, например, представьте, что я определил другой UIView с именем someBlankUIView. В этом UIView нет подпредставлений, но я вызываю эту функцию doSomething() следующим образом: someBlankUIView.doSomething() после добавления его в качестве подпредставления и ожидания вызова UIViewController viewDidLayoutSubviews(). Это НЕ приводит к сбою сразу и работает как задумано.

ОДНАКО следующие действия приведут к сбою:
- как только вы заставите клавиатуру появиться с becomFirstResponder()
- нажмите UITextField / UITextView
- вызвать функцию типа layoutIfNeeded(), setNeedsLayout() и т. д. для self.view UIViewController

ИЗВЕСТНЫЕ АВАРИИ: Если оставить это удаление всех подслоев, следующее приведет к множеству сбоев. Примеры включают в себя:
EXC_BAD_ACCESS
NSArrayI позиция
om_wf isHidden
__NSCFString isHidden
-__ NSOrderedSetM isHidden:

Следует отметить, что некоторые из них имеют одно и то же ключевое слово isHidden, но в то же время эти ошибки отображаются на первый взгляд случайным образом, так как код не меняется между сбоями, а другая причина отображается в журнале.

РЕШЕНИЕ:

Просто никогда не удаляете слой из подслоев? Даже если вы вручную вставили один. Это звучит несколько необычно и, возможно, требует дополнительных исследований, возможно.

Но опять же, как я уже сказал, это решение может выходить за рамки первоначального вопроса, но я подумал, что должен опубликовать его, поскольку оно напрямую вызывает сбой.

ДОПОЛНИТЕЛЬНЫЕ ЗАМЕЧАНИЯ:

Также, чтобы уточнить, это, скорее всего, не имеет ничего общего с обновлением Xcode 9.3.1. Из опыта обновления всегда что-то ломается для меня, и на этот раз я предположил, что это было снова, так как я обновил сразу после добавления этой единственной строки кода. (Мне пришлось обновить Xcode, так как я обновил свой iPhone, что вызвало проблемы совместимости с моей текущей версией Xcode). Надеюсь, я был достаточно наглядным.

...