Xcode 9.3.1 - iOS Swift 4.1 UITextView.becomeFirstResponder 线程 1:EXC_BAD_ACCESS(代码=1,地址=0x4d555458)

Posted

技术标签:

【中文标题】Xcode 9.3.1 - iOS Swift 4.1 UITextView.becomeFirstResponder 线程 1:EXC_BAD_ACCESS(代码=1,地址=0x4d555458)【英文标题】:Xcode 9.3.1 - iOS Swift 4.1 UITextView.becomeFirstResponder Thread 1: EXC_BAD_ACCESS (code=1, address=0x4d555458) 【发布时间】:2018-05-17 16:10:57 【问题描述】:

更新到 Xcode 9.3.1 后,通过手动使 UITextView 成为FirstResponder 出现键盘时,我遇到了 Thread BAD_ACCESS 崩溃。

需要明确的是,这在将 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()
        
    

这是崩溃输出:

*** 由于未捕获的异常“NSInvalidArgumentException”而终止应用程序,原因:“-[__NSCFString isHidden]:无法识别的选择器发送到实例 0x1d0224560” *** 首先抛出调用堆栈: (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:以 NSException 类型的未捕获异常终止

无论我在 self.view 上使用 UITapGestureRecognizer 还是手动点击 UITextView 本身,都会发生此崩溃。两者都会导致相同的崩溃。

注意:在某些情况下,我观​​察到实际的崩溃输出是不同的。在这种情况下,它显示“[__NSCFString isHidden]:”。在其他情况下,输出为“[__NSArrayI position]:”。但是,我没有随时更改代码。

但它总是某种形式的 EXC_BAD_ACCESS。

编辑:完整堆栈跟踪

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

【问题讨论】:

你不删除Notification的观察? @Larme 删除观察也没有改变。事实上,如果我将通知完全注释掉,崩溃仍然会发生。 尝试从 will/didAppear 调用它。 你能包含完整的堆栈跟踪吗?我看不到任何会使blankTextView 变成僵尸的东西,所以它可能发生在文本视图的内部。 @matt UIView.animate 语句对于以与键盘动画时间相同的时间间隔为 NSLayoutConstraint 设置动画是必需的。还调用 self.view.layoutIfNeeded 可以实现 NSLayoutConstraint 的平滑动画。 【参考方案1】:

我想通了……

我想首先说解决方案不在问题的范围内,但我认为无论如何我都应该发布它,因为它可能会在不知不觉中影响似乎与之相关的其他 UI 元素,从而直接导致我的问题。

首先,我在其他地方为 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 调用此扩展函数(不是 UIViewController 的 self.view 而是自定义 UIView)。我有一个要求,我需要用另一个 CALayer 替换这个子层,基本上是从头开始。所以我清除了当前的一个,如果有的话。

例如,假设我定义了另一个 UIView,名为 someBlankUIView。此 UIView 中没有子视图,但我在其上调用此 doSomething() 函数,如下所示:someBlankUIView.doSomething() 在将其添加为子视图并等待 UIViewController 调用 viewDidLayoutSubviews() 之后。 这不会立即导致崩溃并按预期工作。

但是,以下操作会导致崩溃: - 一旦你让键盘出现becomFirstResponder() - 点击 UITextField/UITextView - 在UIViewController的self.view上调用layoutIfNeeded()setNeedsLayout()等函数

已知崩溃: 如果保留所有子层的删除,以下将导致多种崩溃。示例包括: EXC_BAD_ACCESS NSArrayI 位置 om_wf isHidden __NSCFString isHidden -__NSOrderedSetM 隐藏:

必须注意,其中一些具有相同的isHidden 关键字,但同时,这些错误似乎是随机显示的,因为代码在崩溃之间没有变化,但在日志中显示了不同的原因。

解决方案:

永远不要从子图层中删除图层?即使您手动插入了一个。这听起来有些不寻常,也许需要更多的研究。

但就像我说的那样,这个解决方案可能超出了原始问题的范围,但我认为我应该发布它,因为它直接导致崩溃。

进一步说明:

另外澄清一下,这可能与更新 Xcode 9.3.1 无关。根据经验,更新总是对我造成破坏,这一次,我认为又是这种情况,因为我在添加这一行代码后立即进行了更新。 (我必须更新 Xcode,因为我更新了我的 iPhone,这导致了与我当时的 Xcode 版本的兼容性问题)。希望我的描述性足够。

【讨论】:

这确实帮助我解决了我的问题!当我删除视图中的所有子层时,我得到了这个,它确实导致了这些难以确定的崩溃。然而,我的解决方案不是从不删除子层,而是只删除我添加的那些。

以上是关于Xcode 9.3.1 - iOS Swift 4.1 UITextView.becomeFirstResponder 线程 1:EXC_BAD_ACCESS(代码=1,地址=0x4d555458)的主要内容,如果未能解决你的问题,请参考以下文章

Xcode 10, Swift 4.2及iOS 12升级适配

将文本分配给UILabel(iOS,Swift 4,Xcode 9)时内存泄漏

找不到开发人员磁盘映像堆栈溢出 xcode 7.2,iOS 9.3.1 [重复]

XCode 9 Swift 4 中没有这样的模块“FBAudienceNetwork”

UIScreen.mainScreen().nativeBounds.height 无法使用 Xcode 7/Swift 2,目标 iOS7

点击文本字段时用选择器视图替换键盘(Swift、Storyboard、iOS 9、Xcode 7)