ios - 应用程序在 sendEvent 方法上崩溃

标签 ios swift uievent

当我在选择几个项目后旋转应用程序两次时,它崩溃了。我已经覆盖了 sendEvent 方法,这就是调试器停止的地方。当我尝试打印事件类型时,它显示了一些奇怪的东西(我认为这是一个不存在的内存位置):

(lldb) print event.type
(UIEventType) $R10 = <invalid> (0xff)

不知何故,我认为这与我处理旋转的方式有关。我有一个主从样式的应用程序,它使用不同类型的导航来显示 pad-landscape、pad-portrait 和 phone。我创建了一个名为 NavigationFlowController 的类,它处理所有导航事件并相应地设置 View 。在旋转时,它会分解 View 树并使用正确的导航重新组合它们

func changeViewHierarchyForDevideAndOrientation(newOrientation:UIInterfaceOrientation? = nil){
    print("MA - Calling master layout method")

    UIApplication.myDelegate().window?.frame = UIScreen.mainScreen().bounds

    let idiom = UIDevice.currentDevice().userInterfaceIdiom
    var orientation:UIInterfaceOrientation!
    if let no = newOrientation{
        orientation = no
    }else{
        orientation = UIApplication.sharedApplication().statusBarOrientation
    }

    print("MA - Breaking up view tree...")

    breakupFormerViewTree([sidebarViewController, listViewController, detailViewController, loginViewController])

    print("MA - Start init navbackbone")

    initNavBackboneControllers()

    guard let _ = UIApplication.myDelegate().currentUser else {
        if idiom == UIUserInterfaceIdiom.Phone{
            currentState = AppState.PHONE
        }else if idiom == UIUserInterfaceIdiom.Pad && UIInterfaceOrientationIsLandscape(orientation){
            currentState = AppState.PAD_LANDSCAPE
        }else if idiom == UIUserInterfaceIdiom.Pad && UIInterfaceOrientationIsPortrait(orientation){
            currentState = AppState.PAD_PORTRAIT
        }

        print("MA - Current user is nil - resetting")

        mainViewController.addChildViewController(loginViewController)

        return
    }

    if idiom == UIUserInterfaceIdiom.Phone{
        currentState = AppState.PHONE

        leftNavigationController?.viewControllers = [listViewController]

        slideViewController?.rearViewController = sidebarViewController
        slideViewController?.frontViewController = leftNavigationController

        slideViewController?.rearViewRevealWidth = 267;

        mainViewController.addChildViewController(slideViewController!)
    }else if idiom == UIUserInterfaceIdiom.Pad && UIInterfaceOrientationIsLandscape(orientation){
        currentState = AppState.PAD_LANDSCAPE

        leftNavigationController!.viewControllers = [sidebarViewController, listViewController]
        rightNavigationController!.viewControllers = [detailViewController]
        detailViewController.navigationItem.leftBarButtonItems = []
        detailViewController.initLayout()

        print("MA - Init split view controller with VCs")

        splitViewController!.viewControllers = [leftNavigationController!, rightNavigationController!]

        mainViewController.addChildViewController(splitViewController!)

    }else if idiom == UIUserInterfaceIdiom.Pad && UIInterfaceOrientationIsPortrait(orientation){
        currentState = AppState.PAD_PORTRAIT

        leftNavigationController!.pushViewController(sidebarViewController, animated: false)
        leftNavigationController!.pushViewController(listViewController, animated: false)

        rightNavigationController!.pushViewController(detailViewController, animated: false)
        rightNavigationController?.setNavigationBarHidden(false, animated: false)

        slideViewController!.rearViewController = leftNavigationController
        slideViewController!.frontViewController = rightNavigationController

        detailViewController.navigationItem.leftBarButtonItem = UIBarButtonItem(title: "< Documenten", style: UIBarButtonItemStyle.Bordered, target: slideViewController, action: "revealToggle:")
        detailViewController.initLayout()
        slideViewController!.rearViewRevealWidth = 350;

        mainViewController.addChildViewController(slideViewController!)
    }

}

func breakupFormerViewTree(vcs:[UIViewController?]){
    for vc in vcs{
        if let vcUnwrapped = vc, _ = vcUnwrapped.parentViewController {
            vcUnwrapped.removeFromParentViewController()
            vcUnwrapped.view.removeFromSuperview()
        }
    }
}

func initNavBackboneControllers(){
    leftNavigationController = UINavigationController()
    leftNavigationController?.navigationBar.barTintColor = UIColor(red: 0.25, green: 0.25, blue: 0.25, alpha: 1.0)
    leftNavigationController?.navigationBar.tintColor = UIColor.whiteColor()
    leftNavigationController?.navigationBar.titleTextAttributes = [NSForegroundColorAttributeName: UIColor.whiteColor()]
    leftNavigationController?.navigationBar.translucent = false

    rightNavigationController = UINavigationController()
    rightNavigationController?.navigationBar.barTintColor = UIColor(red: 0.25, green: 0.25, blue: 0.25, alpha: 1.0)
    rightNavigationController?.navigationBar.tintColor = UIColor.whiteColor()
    rightNavigationController?.navigationBar.titleTextAttributes = [NSForegroundColorAttributeName: UIColor.whiteColor()]
    rightNavigationController?.navigationBar.translucent = false

    slideViewController = SWRevealViewController()
    slideViewController?.rearViewRevealOverdraw = 0;
    slideViewController?.bounceBackOnOverdraw = false;
    slideViewController?.stableDragOnOverdraw = true;
    slideViewController?.delegate = self

    if UIDevice.currentDevice().userInterfaceIdiom == UIUserInterfaceIdiom.Pad{
        splitViewController = UISplitViewController()
    }
}

编辑(回答贾斯汀的问题):

1) 我在所有 iOS8 iPad 模拟器上都经历过崩溃。

2) 从新开始,如果我选择 6-7 个项目然后旋转两次,它会崩溃。但我也可以选择一个项目,旋转几次,再选择一些并继续旋转,有时它会崩溃。

3) 当一个项目被选中时,下面的代码被执行:

func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    let document = getInfoForSection(indexPath.section).documents[indexPath.item]
    if document.canOpen{
        openDocument(document)

        DataManager.sharedInstance.getDocument(document.uri, after: {
            (document:Document?) -> () in
            if let documentUnwrapped = document{
                let detailVC = NavigationFlowController.sharedInstance.detailViewController;
                if detailVC.document?.uri == documentUnwrapped.uri{
                    NavigationFlowController.sharedInstance.detailViewController.documentUpdated(documentUnwrapped)
                }
            }
        })
    }
}

然后在详细 View Controller 中:

func initLayout(){
    if viewForCard == nil{
        // views not yet initialized, happens when initLayout if called from the document setter before this view has been loaded
        // just return, the layouting will be done on viewDidLoad with the correct document instead
        return
    }

    self.navigationItem.rightBarButtonItems = []

    if document == nil{
        // Removed code that handles no document selected
        ...
        return
    }

    heightForCard.constant = NavigationFlowController.sharedInstance.currentState == AppState.PHONE ? CARD_HEIGHT_PHONE : CARD_HEIGHT_TABLET

    viewForCard.hidden = false
    removeAllSubviews(viewForCard)
    removeAllSubviews(viewForDetails)

    viewForDetails.translatesAutoresizingMaskIntoConstraints = false

    self.metaVC?.document = document
    //self.documentVC?.document = document
    self.navigationItem.rightBarButtonItems = []

    downloadDocumentIfNeeded()

    if NavigationFlowController.sharedInstance.currentState == AppState.PAD_LANDSCAPE || NavigationFlowController.sharedInstance.currentState == AppState.PAD_PORTRAIT{
        self.viewForDetails.backgroundColor = document?.senderStyling?.color

        addChildViewController(self.metaVC!)
        addChildViewController(self.documentVC!)

        let metaView = self.metaVC!.view
        let documentView:UIView = self.documentVC!.view

        viewForDetails.addSubview(metaView)
        viewForDetails.addSubview(documentView)

        // whole lot of layouting code removed
        ...            

        let doubleTap = UITapGestureRecognizer(target: self, action: "toggleZoom")
        documentVC!.view.addGestureRecognizer(doubleTap)

    }else{
        // Phone version code removed
        ...
    }   
}

编辑2:

func downloadDocumentIfNeeded(){
    var tmpPath:NSURL?
    if let url = document?.contentUrl{
        let directoryURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]

        if let docName = self.document?.name,
            safeName = disallowedCharacters?.stringByReplacingMatchesInString(docName, options: [], range: NSMakeRange(0, docName.characters.count), withTemplate: "-"){
                tmpPath = directoryURL.URLByAppendingPathComponent("\(safeName)_\(DetailViewController.dateFormatter.stringFromDate(self.document!.creationDate!)).pdf")

        }


        if let urlString = tmpPath?.path{
            if NSFileManager.defaultManager().fileExistsAtPath(urlString) {
                // File is there, load it
                loadDocumentInWebview(tmpPath!)
            }else{
                // Download file
                let destination: (NSURL, NSHTTPURLResponse) -> (NSURL) = {
                    (temporaryURL, response) in

                    if let path = tmpPath{
                        return path
                    }

                    return temporaryURL
                }

                download(.GET, URLString: url, destination: destination).response {
                    (request, response, data, error) in

                    if error != nil && error?.code != 516{
                        ToastView.showToastInParentView(self.view, withText: "An error has occurred while loading the document", withDuaration: 10)
                    }else if let pathUnwrapped = tmpPath {
                        self.loadDocumentInWebview(pathUnwrapped)
                    }
                }
            }
        }
    }
}

func loadDocumentInWebview(path:NSURL){
    if self.navigationItem.rightBarButtonItems == nil{
        self.navigationItem.rightBarButtonItems = []
    }

    self.documentVC?.finalPath = path

    let shareItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Action, target: self, action: "share")
    shareItem.tag = SHARE_ITEM_TAG

    addNavItem(shareItem)

}

func addNavItem(navItem:UIBarButtonItem){
    var addIt = true
    for item in self.navigationItem.rightBarButtonItems!{
        if item.tag == navItem.tag{
            addIt = false
        }
    }

    if addIt{
        self.navigationItem.rightBarButtonItems?.append(navItem)
        self.navigationItem.rightBarButtonItems!.sortInPlace({ $0.tag > $1.tag })
    }
}

EDIT3:我已经覆盖了 sendEvent 方法来跟踪用户是否正在触摸应用程序,但即使我删除这段代码,它仍然会崩溃,然后调试器会在 UIApplicationMain 上中断。

override func sendEvent(event: UIEvent)
{        
    super.sendEvent(event)

    if event.type == UIEventType.Touches{
        if let touches = event.allTouches(){
            for item in touches{
                if let touch = item as? UITouch{
                    if touch.phase == UITouchPhase.Began{
                        touchCounter++
                    }else if touch.phase == UITouchPhase.Ended || touch.phase == UITouchPhase.Cancelled{
                        touchCounter--
                    }

                    if touchCounter == 0{
                        receiver?.notTouching()
                    }
                }
            }
        }
    }
}

最佳答案

很难,对导致此错误的事件有更多的了解可能会有所帮助。

  1. 它是否在每台设备上都会发生(如果没有,哪些设备会给您带来麻烦)
  2. 它发生在“大力选择”项目之后。您的设备在此之前是否改变了方向。它是否也发生在你旋转之前?
  3. 当您“选择一个项目”时,您在代码中做了什么。

除此之外,我将开始了解在 breakupFormerViewTree() 中移除子 ViewController 的流程。根据 Apple Docs 你想告诉 child 它正在被删除,然后再删除 View ,然后最终从父 ViewController 中删除 child

https://developer.apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/CreatingCustomContainerViewControllers/CreatingCustomContainerViewControllers.html

这里实际上说你想在删除之前调用 willMoveToParentViewController(nil)。它没有说明如果不这样做会发生什么,但我可以想象操作系统会在那里进行一些生命周期管理,以防止它在以后发送损坏的事件。

https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewController_Class/index.html#//apple_ref/occ/instm/UIViewController/willMoveToParentViewController :

编辑(附加代码发布后)

我在您的代码中没有看到任何其他可能导致它崩溃的内容。正如您所说,它确实看起来像内存错误,但不知道它来自哪里。尝试打开 Zombie objects 和 Guard Malloc(Scheme > Run > Diagnostics),也许您可​​以获得有关导致它的原因的更多信息。

除此之外,我只是注释掉您的大量实现,用空的 ViewController 交换子类,直到它不再发生。您应该能够确定创建此事件所涉及的实现部分。一旦你这样做了,好吧,查明更多并评估该实现中的每一行代码。

关于ios - 应用程序在 sendEvent 方法上崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32301167/

相关文章:

iphone - EGOTableViewPullRefresh - 由于链接问题无法构建

objective-c - 从 Core Data 检索 NSDate 时的时区问题

swift - 随机化节点移动持续时间

ios - event.touchesForView().AnyObject() 在 Xcode 6.3 中不起作用

javascript - setTimeout 和 UIEvent 顺序

ios - 无法从可选中获取 'some' 字段

ios - 如何绘制按钮 Storyboard?

arrays - 如何快速卡住数组?

ios - 如何将多个观察者绑定(bind)到一个 ControlProperty

iOS//从任何播放器以编程方式跳过歌曲