使用带有自定义容器视图控制器的滑动手势识别器崩溃

Posted

技术标签:

【中文标题】使用带有自定义容器视图控制器的滑动手势识别器崩溃【英文标题】:Crash using Swipe Gesture Recognizer with Custom Container View Controller 【发布时间】:2017-12-01 03:46:03 【问题描述】:

XCode 9.1 iPhoneX模拟器 iPhone6 11.1.2

我已经浪费了好几天的时间来试图追踪一个似乎表现为内存损坏的崩溃(因为它似乎每次都不一样)。

我已将其范围缩小为使用带有滑动手势识别器的(简单)容器视图控制器。

您可以在导航栏中看到两个(左和右)滑动手势识别器以及两个(上一个和下一个)按钮。 UIViewController 有一个简单的UILabel

手势识别器配置为:

过渡由以下人员执行:

  private func addViewToLayout(view: UIView) 
    let views = ["view": view]

    view.translatesAutoresizingMaskIntoConstraints = false
    container.addSubview(view)

    container.addConstraints(
        NSLayoutConstraint.constraints(withVisualFormat: "V:|[view]|",
        options:NSLayoutFormatOptions.alignAllLeft, metrics:nil, views:views))
    container.addConstraints(
        NSLayoutConstraint.constraints(withVisualFormat: "H:|[view]|",
        options:NSLayoutFormatOptions.alignAllLeft, metrics:nil, views:views))
  

  private func transition(direction: Direction = .left) 
    let controllerPrevious = childViewControllers.first

    if let controllerNext = storyboard?.instantiateViewController(
        withIdentifier: "Test") 
      addChildViewController(controllerNext)
      addViewToLayout(view: controllerNext.view)

      if let controllerPrevious = controllerPrevious 
        controllerNext.view.transform = startTransform(direction: direction)

        UIView.animate(withDuration: transitionDuration,
            animations: 
              controllerNext.view.transform = CGAffineTransform.identity
              controllerPrevious.view.transform =
                  self.endTransform(direction: direction)
            ,
            completion:  (finished: Bool) in
              controllerPrevious.view.removeFromSuperview()
              controllerPrevious.removeFromParentViewController()
              controllerNext.didMove(toParentViewController: self)
            )
      
    
  

崩溃可能发生在转换期间/之后,或者在离开 Container View Controller 之后发生。调试器通常在没有有意义的堆栈跟踪的主线程中结束。

崩溃相对容易导致,但并不总是由相同的操作发生,例如(在设备上运行得到的日志):

EXC_BAD_ACCESS (SIGSEGV)
KERN_INVALID_ADDRESS at 0x4054600000000000
Thread 0 name:  Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0   libobjc.A.dylib                 0x0000000180f68428 objc_msgSend + 8
1   UIKit                           0x000000018b40dda4 -[UIView _layoutConstraintCleanup] + 204

EXC_CRASH (SIGABRT)
0x0000000000000000, 0x0000000000000000
EXC_CORPSE_NOTIFY
Thread 0 Crashed:
0   libsystem_kernel.dylib          0x000000018181d348 __pthread_kill + 8
1   libsystem_pthread.dylib         0x0000000181931344 pthread_kill$VARIANT$mp + 396
2   libsystem_c.dylib               0x000000018178cfb8 abort + 140
3   libsystem_malloc.dylib          0x0000000181863a08 nanozone_default_reader + 0

请注意,如果仅使用导航栏中的按钮,不会发生崩溃。

有什么可以帮助找出崩溃的原因吗?

在转换后释放当前子视图控制器时,手势识别器是否需要摆弄?

这个question 似乎有类似的症状,但实际上并没有任何有用的建议。

编辑:

我已将一个显示崩溃的项目上传到Dropbox。编译项目,运行,点击下一步,使用两次左右滑动多次,点击返回,点击下一步,重复直到崩溃。有时崩溃是立即发生的,有时可能需要 20 次或更多操作。

【问题讨论】:

在过渡中的完成块中 - 您可以删除该部分并查看崩溃是否仍然发生?我觉得您可能会遇到竞争状况,这就是错误不一致的原因。 (不是在我的开发 mac 或 id 测试它更多) 仅供参考,有一个苹果提供的方法来做自定义动画过渡developer.apple.com/documentation/uikit/… @solenoid 我尝试删除removeFromParentViewController,然后也删除了didMove。崩溃仍然发生。 我已经提交了一个错误 35792348。也许 Apple 的某个人可以弄清楚发生了什么。 【参考方案1】:

从您提供的有限数据来看,您似乎有一个内存粉碎机。

您应该使用 Xcode 提供的工具来调试此类问题并提供调查结果。对您的项目进行静态分析。与 ASan、NSZombies、GuardMalloc 等一起运行。

如果您提供更多此类调查的结果,将更容易帮助您确定后续步骤。

【讨论】:

我已打开所有警告 ("-Weverything"),已分析,已打开消毒剂、僵尸、GuardMalloc。根本没有结果。我认为“内存粉碎机”是手势识别器与从容器视图中添加和删除的视图/控制器之间的一些交互。 我尝试使用 Instruments,但我不知道我在做什么。没有观察到任何明显的东西。请参阅主要问题中的编辑,了解您可以运行以观察崩溃的项目。【参考方案2】:

这可能很简单,但我在使用 Interface Builder 和 2 次手指滑动时遇到了问题。当我为它分配 1 个手指时,它工作正常,但它在 2 个手指上崩溃。

如果我在 IB 中将其保留为 1 个手指并在 viewDidLoad 中将其更改为 2 个手指,我的问题就会消失。

【讨论】:

以上是关于使用带有自定义容器视图控制器的滑动手势识别器崩溃的主要内容,如果未能解决你的问题,请参考以下文章

无法识别滑动手势

使用平移手势识别器关闭视图控制器

使用不跟随拇指的屏幕边缘平移手势识别器的弹出视图控制器

使用滑动手势识别器从一个视图控制器滑动到另一个

iOS - 自定义 segue 内存泄漏

防止自定义 UITableViewCells 中的多个平移手势