Сбой приложения при методе sendEvent

Когда я дважды поворачиваю приложение после выбора нескольких элементов, оно вылетает. Я переопределил метод sendEvent, и на этом отладчик останавливается. Когда я пытаюсь напечатать тип события, он показывает мне что-то странное (я думаю, что это место в памяти, которого не существует):

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

Почему-то я думаю, что это связано с тем, как я справляюсь с вращением. У меня есть приложение в стиле master-detail, в котором используется другой тип навигации для pad-landscape, pad-portrait и phone. Я создал класс с именем NavigationFlowController, который обрабатывает все навигационные события и соответствующим образом настраивает представления. При вращении он разбивает деревья представлений и перекомпоновывает их с правильной навигацией.

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)
                }
            }
        })
    }
}

А затем в контроллере подробного представления:

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()
                    }
                }
            }
        }
    }
}

person Anthony De Smet    schedule 30.08.2015    source источник
comment
Можешь показать downloadDocumentIfNeeded() ?   -  person tuledev    schedule 08.09.2015
comment
Что именно вы делаете с событиями? Я не вижу там sendEvent?   -  person Sulthan    schedule 08.09.2015
comment
Я объяснил в EDIT3   -  person Anthony De Smet    schedule 08.09.2015
comment
Вы проверили все принудительно развернутые (!) опции..   -  person Swapnil Luktuke    schedule 08.09.2015
comment
Пока нет, хотя большинство из них кажутся безопасными, и если это было причиной сбоя, не остановился бы отладчик на одном из них?   -  person Anthony De Smet    schedule 08.09.2015


Ответы (1)


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

  1. Это происходит на каждом устройстве (если нет, какие устройства доставляют вам проблемы)
  2. Это происходит после "энергичного выбора" предметов. Изменяло ли ваше устройство ориентацию перед этим. Это также происходит до того, как вы один раз повернетесь?
  3. Что вы делаете в коде, когда «выбираете элемент».

Кроме того, я бы начал правильно удалять ваши дочерние ViewControllers в breakupFormerViewTree(). Основываясь на Apple Docs, вы хотите сообщить дочернему элементу, что он удаляется, прежде чем удалять представление, а затем, наконец, удалить дочерний элемент из Parent ViewController.

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:

ИЗМЕНИТЬ (после того, как дополнительный код был опубликован)

Я не вижу в вашем коде ничего, что могло бы привести к его сбою. Как вы сказали, это похоже на ошибку памяти, но не знаю, откуда она взялась. Попробуйте включить объекты-зомби и Guard Malloc (Схема > Выполнить > Диагностика), и, возможно, вы сможете получить немного больше информации о том, что вызывает это.

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

person Justin Hammenga    schedule 02.09.2015