了解 crashlytics UIKit 错误

Understanding crashlytics UIKit errors

我无法理解我收到的 UIKit 崩溃报告:

有没有办法找出是哪一行代码导致的:

Crashed: com.apple.main-thread
0  UIKit                          0x195694264 __56-[UIPresentationController runTransitionForCurrentState]_block_invoke + 444
1  UIKit                          0x1955d0950 _runAfterCACommitDeferredBlocks + 292
2  UIKit                          0x1955c29ec _cleanUpAfterCAFlushAndRunDeferredBlocks + 528
3  UIKit                          0x195336648 _afterCACommitHandler + 132
4  CoreFoundation                 0x18f1c09a8 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 32
5  CoreFoundation                 0x18f1be630 __CFRunLoopDoObservers + 372
6  CoreFoundation                 0x18f1bea7c __CFRunLoopRun + 956
7  CoreFoundation                 0x18f0eeda4 CFRunLoopRunSpecific + 424
8  GraphicsServices               0x190b58074 GSEventRunModal + 100
9  UIKit                          0x1953a9058 UIApplicationMain + 208
10 FlexConnect                    0x1001b48c8 main (AppDelegate.swift:20)
11 libdyld.dylib                  0x18e0fd59c start + 4

错误本身是:

崩溃:com.apple.main-thread

EXC_BAD_ACCESS KERN_INVALID_ADDRESS 0x0000000000000010

编辑:

根据下面的回答,我想知道是否建议使用:

func topMostController() -> UIViewController {
    var topController: UIViewController = UIApplication.sharedApplication().keyWindow!.rootViewController!
    while (topController.presentedViewController != nil) {
        topController = topController.presentedViewController!
    }
    return topController
}

并始终调用

let topVC = topMostController().dismiss(animated: true, completion: nil)

在我的应用程序中我目前拥有 self.dismiss(动画:真实,完成:无)的任何地方?

这是必要的检查吗?或者我如何确定 self.dismiss 哪里有问题?

一些示例解雇:

@IBAction func returnToDash(_ sender: UIButton) {
     self.dismiss(animated: true, completion: nil)
}

let pending = UIAlertController(title: "\n\n\n\(title)", message: nil, preferredStyle: .alert)
displayActivityAlertWithCompletion2(ViewController: self, pending: pending){_ in
   Helper_StatusCheck.doSync(_cleanSync: false){
        Prefs.is_Syncing = false
        DispatchQueue.main.async {
            pending.dismiss(animated: true){
                   Toast(text: "Upload sync completed").show()
                   self.dismiss(animated: true, completion: nil)
            }
        }
   }
}

其中 displayActivityAlertWithCompletion2 看起来像:

public func displayActivityAlertWithCompletion2(ViewController: UIViewController, pending: UIAlertController, completionHandler: @escaping ()->())
{
    //let pending = UIAlertController(title: "\n\n\n"+title, message: nil, preferredStyle: .alert)
    //create an activity indicator
    let indicator = UIActivityIndicatorView(frame: pending.view.bounds)
    indicator.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    indicator.color = UIColor(rgba: Palette.loadingColour)
    //add the activity indicator as a subview of the alert controller's view
    pending.view.addSubview(indicator)
    indicator.isUserInteractionEnabled = false
    // required otherwise if there buttons in the UIAlertController you will not be able to press them
    indicator.startAnimating()



    ViewController.present(pending, animated: true, completion: completionHandler)
}

编辑 2:

我的应用程序中的一些示例弹出方法:

 @IBAction func search(_ sender: UIButton) {
        if let popView = UIStoryboard(name: "AssetCommon", bundle: nil).instantiateViewController(withIdentifier: "searchPop") as? searchPopVC {
            popView.delegate = self
            popView.modalPresentationStyle = .popover;
            popView.popoverPresentationController?.delegate = self
            popView.popoverPresentationController?.barButtonItem = searchButton
            popView.popoverPresentationController?.permittedArrowDirections = .any
            popView.preferredContentSize = CGSize(width: 300, height: 70)
            self.present(popView, animated: true, completion: nil)
        }
    }

并搜索 pop:

class searchPopVC: UIViewController
{
    @IBOutlet weak var searchBar: UISearchBar!

    weak var delegate: SearchPopDelegate?

    override func viewDidLoad()
    {
        super.viewDidLoad()

        searchBar.delegate = self;
    }

    override func didReceiveMemoryWarning()
    {
        super.didReceiveMemoryWarning()
    }

    @IBAction func performSearch(_ sender: UIButton)
    {
        let term = searchBar.text ?? "";
        delegate?.performSearch(with: term)
        self.dismiss(animated: true, completion: nil);
    }
}

extension searchPopVC: UISearchBarDelegate
{
    func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
        let term = searchBar.text ?? "";
        delegate?.performSearch(with: term);
        self.dismiss(animated: true, completion: nil)
    }
}

您将无法找到此次崩溃的代码行。但是,当您用来调用 dismiss(animated:completion:) 的视图控制器在动画完成之前从视图层次结构中删除时,就会发生此崩溃。

根据您的代码设置方式,您可以尝试请求更高层的视图控制器来调用解雇。另一种解决方案是将视图控制器保留在 属性 中,直到您确定使用它为止。

编辑:1/5/18

作为对评论的回应,这里是一个示例,说明如何为记录事件并关闭的视图控制器创建一个函数。

extension UIViewController {
    func dismissAndLog(animated: Bool, completion: (() -> ())? = nil) {
        // Here are two examples of how your view controller can be identified.
//        let id = title
        let id = String(describing: type(of: self))
        CLSLogv("Dismissed View Controller: %@", getVaList([id]))

        dismiss(animated: animated, completion: completion)
    }
}

我对 Crashlytics 或其 API 并不完全熟悉,因此如果此日志记录给您带来问题,您可以查看这些链接。

另外我不知道他们是如何向你提供数据的,所以我无法向你解释解析它的最佳方式。但是,当然可以将时间戳成功地用作最终解决方案。您也可以尝试通过电子邮件向他们的支持人员发送电子邮件,询问将这些日志映射到崩溃的最佳方式。

var topController: UIViewController = UIApplication.sharedApplication().keyWindow!.rootViewController!

这么多感叹号应该告诉您这段代码有一些不好的地方。警惕并检查这些假设,因为它们中的任何一个都可能爆炸。

除此之外,如果您将这些 dismiss/pop/present 操作作为延迟块的一部分,就像您在 Helper_StatusCheck.doSync 中所做的那样,会给您带来一些非常奇怪的错误。导航堆栈可能会有各种变化,您的假设可能不成立。