Периодически происходит сбой heightForRowAtIndexPath

У меня есть приложение с примерно 30 активными пользователями и более 500 зарегистрированными пользователями, и эта проблема возникает только у одного человека, который использует версию 9.2 и использует iPhone 6.

Я вижу из отчетов о сбоях, у нее было более 60 сбоев, и она сказала, что иногда проблема в порядке, а в других нет.

У меня есть такое же тестовое устройство, и никаких проблем не происходит.

Я не могу получить много информации из отчета о сбоях из Fabric, но сбой происходит в следующем методе, и приложение падает в конце else этого метода, я могу сказать из ее описания проблема:

func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
    {
        let bounds = UIScreen.mainScreen().bounds
        var screenWidth = bounds.size.width
        let screenHeight = bounds.size.height
        if indexPath.row == 0 {
            let font = UIFont(name: "Helvetica Neue", size: 17.0)
            let heightCalc = heightForView(self.postDescription!, font: font!, width: screenWidth - 32)
            if self.photoWidth != nil{

                let imageAspectRatio = self.photoWidth!/self.photoHeight!
                var imageViewFrameHeight = screenWidth/imageAspectRatio
                if self.portBool {
                    if imageViewFrameHeight > (screenHeight - 64) {
                        screenWidth = screenWidth * 0.60
                        imageViewFrameHeight = imageViewFrameHeight * 0.60
                    }
                }

                return imageViewFrameHeight + 136 + heightCalc + 26
                //return 557
            }
            else{
                return 136 + heightCalc + 30
            }
        }
        else if indexPath.row == 1 {
            return 50.0
        }
        else if indexPath.row == 2{
            return 40.0
        }
        else{

            if self.commentsDetailed != nil && self.commentsDetailed!.count >= (indexPath.row - 3)
            {
                let commentsDetailed = self.commentsDetailed![indexPath.row-3]
                let font = UIFont(name: "Helvetica Neue", size: 14.0)

                let heightCalc = heightForView(commentsDetailed.comment!, font: font!, width: screenWidth - 51)

                if commentsDetailed.authorId == Prefs.userId.description {
                    return 45 + heightCalc + 40 + 30
                    //return 131.5
                }
                else{
                    return 45 + heightCalc + 40
                    //return 101.5
                }

            }
            else {
                return 80.0
            }

        }
    }

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

Немного информации от crashlytics:

EXC_BREAKPOINT

Thread : Crashed: Thread
0  ProjectName                   0x10008a64c specialized NewFeedDetailedController.tableView(UITableView, heightForRowAtIndexPath : NSIndexPath) -> CGFloat (NewFeedDetailedController.swift)
1  ProjectName                   0x100085258 @objc NewFeedDetailedController.tableView(UITableView, heightForRowAtIndexPath : NSIndexPath) -> CGFloat (NewFeedDetailedController.swift)
2  UIKit                          0x187683638 __66-[UISectionRowData refreshWithSection:tableView:tableViewRowData:]_block_invoke + 396
3  UIKit                          0x187641818 -[UISectionRowData refreshWithSection:tableView:tableViewRowData:] + 3948
4  UIKit                          0x1876407d4 -[UITableViewRowData rectForFooterInSection:heightCanBeGuessed:] + 412
5  UIKit                          0x187640590 -[UITableViewRowData heightForTable] + 64
6  UIKit                          0x18764039c -[UITableView _updateContentSize] + 220
7  UIKit                          0x1878a6f60 -[UITableView _rebuildGeometry] + 44
8  UIKit                          0x1876462c8 -[UITableView didMoveToWindow] + 144
9  UIKit                          0x18755705c -[UIView(Internal) _didMoveFromWindow:toWindow:] + 1496
10 UIKit                          0x18757c568 -[UIScrollView _didMoveFromWindow:toWindow:] + 92
11 UIKit                          0x187556d7c -[UIView(Internal) _didMoveFromWindow:toWindow:] + 760
12 UIKit                          0x187556310 __45-[UIView(Hierarchy) _postMovedFromSuperview:]_block_invoke + 152
13 Foundation                     0x183189500 -[NSISEngine withBehaviors:performModifications:] + 168
14 UIKit                          0x187556194 -[UIView(Hierarchy) _postMovedFromSuperview:] + 532
15 UIKit                          0x187563b80 -[UIView(Internal) _addSubview:positioned:relativeTo:] + 1784
16 UIKit                          0x187755600 -[_UIParallaxDimmingView didMoveToWindow] + 180
17 UIKit                          0x18755705c -[UIView(Internal) _didMoveFromWindow:toWindow:] + 1496
18 UIKit                          0x187556d7c -[UIView(Internal) _didMoveFromWindow:toWindow:] + 760
19 UIKit                          0x187556310 __45-[UIView(Hierarchy) _postMovedFromSuperview:]_block_invoke + 152
20 Foundation                     0x183189500 -[NSISEngine withBehaviors:performModifications:] + 168
21 UIKit                          0x187556194 -[UIView(Hierarchy) _postMovedFromSuperview:] + 532
22 UIKit                          0x187563b80 -[UIView(Internal) _addSubview:positioned:relativeTo:] + 1784
23 UIKit                          0x1877f5db4 __53-[_UINavigationParallaxTransition animateTransition:]_block_invoke_2 + 1656
24 UIKit                          0x18756a964 +[UIView(Animation) performWithoutAnimation:] + 80
25 UIKit                          0x187755118 __53-[_UINavigationParallaxTransition animateTransition:]_block_invoke + 260
26 UIKit                          0x187870840 +[UIView(Internal) _performBlockDelayingTriggeringResponderEvents:] + 220
27 UIKit                          0x187754c90 -[_UINavigationParallaxTransition animateTransition:] + 1060
28 UIKit                          0x18770e6a0 -[UINavigationController _startCustomTransition:] + 3544
29 UIKit                          0x18761a9b8 -[UINavigationController _startDeferredTransitionIfNeeded:] + 688
30 UIKit                          0x18761a694 -[UINavigationController __viewWillLayoutSubviews] + 60
31 UIKit                          0x18761a5fc -[UILayoutContainerView layoutSubviews] + 208
32 UIKit                          0x187557778 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 656
33 QuartzCore                     0x184f66b2c -[CALayer layoutSublayers] + 148
34 QuartzCore                     0x184f61738 CA::Layer::layout_if_needed(CA::Transaction*) + 292
35 QuartzCore                     0x184f615f8 CA::Layer::layout_and_display_if_needed(CA::Transaction*) + 32
36 QuartzCore                     0x184f60c94 CA::Context::commit_transaction(CA::Transaction*) + 252
37 QuartzCore                     0x184f609dc CA::Transaction::commit() + 512
38 UIKit                          0x18754dc78 _afterCACommitHandler + 180
39 CoreFoundation                 0x182820588 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 32
40 CoreFoundation                 0x18281e32c __CFRunLoopDoObservers + 372
41 CoreFoundation                 0x18281e75c __CFRunLoopRun + 928
42 CoreFoundation                 0x18274d680 CFRunLoopRunSpecific + 384
43 GraphicsServices               0x183c5c088 GSEventRunModal + 180
44 UIKit                          0x1875c4d90 UIApplicationMain + 204
45 ProjectName                   0x1001663b0 main (AppDelegate.swift:21)
46 libdyld.dylib                  0x1822ee8b8 start + 4 

И функция heightForView:

func heightForView(text:String, font:UIFont, width:CGFloat) -> CGFloat{
        let label:UILabel = UILabel(frame: CGRectMake(0, 0, width, CGFloat.max))
        label.numberOfLines = 0
        label.lineBreakMode = NSLineBreakMode.ByWordWrapping
        label.font = font
        label.text = text

        label.sizeToFit()
        return label.frame.height
    }

Обновленный код с проверкой нуля, по-прежнему происходит сбой для пользователя:

func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
    {
        let bounds = UIScreen.mainScreen().bounds
        var screenWidth = bounds.size.width
        let screenHeight = bounds.size.height
        if indexPath.row == 0 {
            CLSLogv("crashing in indexPath.Row = 0 %@", getVaList(["returning 50.0 here"]))

            let font = UIFont(name: "Helvetica Neue", size: 17.0)

            if let description = self.postDescription {
                let heightCalc = heightForView(description, font: font!, width: screenWidth - 32)
                if self.photoWidth != nil && self.photoHeight != nil {
                    CLSLogv("crashing in indexPath.Row = 0 %@", getVaList(["inside photowidth is not nil"]))
                    let imageAspectRatio = self.photoWidth!/self.photoHeight!
                    var imageViewFrameHeight = screenWidth/imageAspectRatio
                    if self.portBool {
                        if imageViewFrameHeight > (screenHeight - 64) {
                            screenWidth = screenWidth * 0.60
                            imageViewFrameHeight = imageViewFrameHeight * 0.60
                        }
                    }

                    return imageViewFrameHeight + 136.0 + heightCalc + 26.0
                    //return 557
                }
                else{
                    CLSLogv("crashing in indexPath.Row = 0 %@", getVaList(["when photowidth is nil"]))
                    return 136.0 + heightCalc + 30.0
                }
            }
            else{
                CLSLogv("crashing in indexPath.Row = 0 %@", getVaList(["unable to unwrap self.postDescription"]))
                if self.photoWidth != nil && self.photoHeight != nil {
                    let imageAspectRatio = self.photoWidth!/self.photoHeight!
                    var imageViewFrameHeight = screenWidth/imageAspectRatio
                    if self.portBool {
                        if imageViewFrameHeight > (screenHeight - 64) {
                            screenWidth = screenWidth * 0.60
                            imageViewFrameHeight = imageViewFrameHeight * 0.60
                        }
                    }

                    return imageViewFrameHeight + 136.0 + 26.0
                }
                else{
                    return 136.0 + 30.0
                }
            }
        }
        else if indexPath.row == 1 {
             CLSLogv("crashing in indexPath.Row = 1 %@", getVaList(["returning 50.0 here"]))
            return 50.0
        }
        else if indexPath.row == 2 {
            CLSLogv("crashing in indexPath.Row = 2 %@", getVaList(["returning 40.0 here"]))
            return 40.0
        }
        else{

            if self.commentsDetailed != nil && self.commentsDetailed!.count > (indexPath.row - 3)
            {

                if let commentsDetailed = self.commentsDetailed?[indexPath.row-3]{


                    let font = UIFont(name: "Helvetica Neue", size: 14.0)
                    if let comment = commentsDetailed.comment{
                        let heightCalc = heightForView(comment, font: font!, width: screenWidth - 51)
                        if commentsDetailed.authorId == Prefs.userId.description {
                            return 45.0 + heightCalc + 40.0 + 30.0
                        }
                        else{
                            return 45.0 + heightCalc + 40.0
                        }
                    }
                    else{
                        if commentsDetailed.authorId == Prefs.userId.description {
                            return 45.0 + 40.0 + 30.0
                        }
                        else{
                            return 45.0 + 40.0
                        }
                    }
                }
                else {
                     CLSLogv("in the else for defining commentsDetailed %a",getVaList(["commentsdetailed"]))
                    return 80.0
                }
            }
            else {
                CLSLogv("in the condition for checking commentsDetailed = nil - true %a", getVaList(["CRASH RELATED TO RETURNING 80 IF ARRAY IS NIL?"]))
                return 80.0
            }

        }
    }

person user2363025    schedule 15.12.2015    source источник
comment
Был ли тип исключения, и вы уверены, что commentDetailed.comment не может быть нулевым?   -  person Naoto Ida    schedule 15.12.2015
comment
@NaotoIda в верхней части отчета о сбое говорит: EXC_BREAKPOINT.   -  person user2363025    schedule 15.12.2015
comment
@NaotoIda Я уверен, что commentsDetailed.comment не может быть нулевым. Я сам делал веб-сервисы   -  person user2363025    schedule 15.12.2015
comment
Вы устанавливали высоту в другом месте? Возможно, в представленииDidLoad? Или в раскадровке или в ячейке? У вас может быть 2 конфликтующих высоты. Кроме того, есть ли у вас какие-либо жесты в вашем приложении?   -  person Lukesivi    schedule 15.12.2015
comment
@lukesIvi Я не устанавливаю глобальную высоту ячейки, нет, просто реализую метод heightForRow. Раскадровка не имеет ограничения по высоте, поскольку размер ячейки является динамическим. У меня есть жесты в ячейках заголовка. Будет ли это связано?   -  person user2363025    schedule 15.12.2015
comment
Вы опубликовали трассировку стека для потока, но не журнал ошибок. Что написано в журнале ошибок? Он скажет что-то вроде Неожиданно найдено nil при распаковке необязательного или что-то в этом роде. Читаемый текст.   -  person Fogmeister    schedule 15.12.2015
comment
@Fogmeister Я не могу воспроизвести проблему и получить журнал ошибок. если бы я мог, это было бы здорово. Я просто получаю отчет о сбое от crashlytics, и я не вижу и хорошо читаемых ошибок, я просто получаю трассировку стека :(   -  person user2363025    schedule 15.12.2015
comment
Это может быть связано с распознавателем жестов @user2363025. Я видел что-то похожее всплывающее окно с проектом коллеги. Можете ли вы протестировать некоторые связанные жесты с этим тестовым устройством? Я также выясняю, что случилось с проектом друга.   -  person Lukesivi    schedule 15.12.2015
comment
Проверял у друга. У него возникла проблема при циклическом просмотре массива на распознавателе жестов. Предполагая, что это не имеет отношения к вашей проблеме. Сообщите об ошибке в Apple.   -  person Lukesivi    schedule 15.12.2015
comment
Как и другие, вы выполняете принудительную распаковку вместо if let ... или guard let ..., это возможные причины, и вы разворачиваете commentsDetailed.comment!, не проверяя, является ли это nil или нет.   -  person Dániel Nagy    schedule 15.12.2015
comment
У них есть взломанное устройство? Это может быть как-то связано со шрифтом, или это может быть ошибка в коде Swift с именем вашей переменной commentsDetailed в let commentsDetailed = self.commentsDetailed![indexPath.row-3]. Попробуйте назвать его как-нибудь иначе, например let commentsDetailedValue = self.commentsDetailed![indexPath.row-3].   -  person Jojodmo    schedule 16.12.2015
comment
@DánielNagy Jojodmo, спасибо за отзыв, я отправил пользователю тестовую сборку с улучшенными журналами сбоев и добавил комментарий к комментариям. люди по понятным причинам считают, что это единственное место, где должна произойти авария. Мне просто нужно подождать и посмотреть, что пользователь получит обратно   -  person user2363025    schedule 16.12.2015
comment
@Fogmeister Я обновлю вопрос своим последним кодом, который проверяет, что все переменные не равны нулю. Пользователь все еще испытывает проблему   -  person user2363025    schedule 18.12.2015
comment
@jojodmo Согласно твиттеру, ее устройство не взломано. Я обновил код для проверки переменных как nil   -  person user2363025    schedule 18.12.2015


Ответы (3)


Я думаю, что единственная возможная точка, где второй else может потерпеть неудачу, — это развертывание commentsDetailed.comment!, которое должно было бы потерпеть неудачу, если бы оно было равно нулю. Остальное кажется совершенно надежным, включая метод heightForView. Если проверка commentsDetailed.comment != nil не поможет, я начну думать, что это какая-то случайная ранняя ошибка 9.2...

person Bartserk    schedule 15.12.2015
comment
Я знаю, что commentsDetailed.comment никогда не может быть нулевым, потому что это встроено в запрос. - person user2363025; 15.12.2015
comment
вряд ли мне нужно использовать «return 45.0 + heightCalc + 40.0 + 30.0» вместо «return 45 + heightCalc + 40 + 30», это единственное, о чем я могу думать, но, конечно, если бы это было проблемой, это не было бы происходить с перерывами, и это не будет характерно для одного пользователя?? - person user2363025; 15.12.2015
comment
Вот почему я подозревал, что это связано либо с данными, либо с SDK. Я еще не работал с версией 9.2, но если это окажется багом ОС, то точно не в первый раз... - person Bartserk; 15.12.2015
comment
спасибо за отзыв, я разослал пользователю тестовую сборку с улучшенными журналами сбоев и добавил комментарий к комментариямDetailed.comment! люди по понятным причинам считают, что это единственное место, где должна произойти авария. Мне просто нужно подождать и посмотреть, что пользователь получит обратно - person user2363025; 16.12.2015
comment
Я обновил код и добавил, если позволяет обойти все, и добавил несколько дополнительных пользовательских журналов с крашлитикой, и у нее все еще происходит сбой. Более того, теперь я не получаю отчеты о сбоях ее устройства :( - person user2363025; 17.12.2015

Ошибка - это массив вне отскока при последнем закрытии else, как вы сказали:

if self.commentsDetailed != nil && self.commentsDetailed!.count >= (indexPath.row - 3)
     {
        let commentsDetailed = self.commentsDetailed![indexPath.row-3]

Если, например, массив имеет размер 1, то последний элемент, который вы можете проиндексировать, - это arr[0], но count >= (indexPath.row - 3) позволит индексировать arr[1], что вызовет ошибку.

Есть и другие исправления, которые вы можете сделать для оптимизации, так как этот метод вызывается неоднократно. (Я бы предложил, возможно, иметь шрифты и другие переменные снаружи, чтобы он не создавал их каждый раз. Возможно, такая оптимизация позади, но я не уверен).

Мне пришлось переписать всю игровую площадку, чтобы найти проблему, поэтому я просто вставлю код сюда :)

func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: 
    NSIndexPath) -> CGFloat {
    var screenWidth = UIScreen.mainScreen().bounds.size.width
    let screenHeight = UIScreen.mainScreen().bounds.size.height

    switch indexPath.row {

    case 0:

        guard let font = UIFont(name: "Helvetica Neue", size: 17.0) else { 
            print("Font not Found !")
            break
        }
        let heightCalc = heightForView(self.postDescription! ?? "", font: font, width: screenWidth - 32)

        guard let photoWidth = self.photoWidth, let photoHeight = self.photoHeight else {
            return 136 + heightCalc + 30
        }
        let imageAspectRatio = photoWidth / photoHeight
        var imageViewFrameHeight = screenWidth / imageAspectRatio
        if self.portBool {
            if imageViewFrameHeight > (screenHeight - 64) {
                screenWidth = screenWidth * 0.60
                imageViewFrameHeight = imageViewFrameHeight * 0.60
            }
        }
        return imageViewFrameHeight + 136 + heightCalc + 26
        //return 557

    case 1: return 50.0
    case 2: return 40.0

    case _ where self.commentsDetailed != nil && indexPath.row >= 3: 

        let index = indexPath.row - 3
        //I think your problem was here (your index can not be equal to the count!)
        guard let detailed = self.commentsDetailed where detailed.count > index
            else { break }

        let commentsDetailed = detailed[index]
        guard let font = UIFont(name: "Helvetica Neue", size: 14.0) else { 
            break //font not found
        }

        let heightCalc = heightForView(commentsDetailed.comment! ?? "", font: font, width: screenWidth - 51)

        if commentsDetailed.authorId == Prefs.userId.description { //make sure author id is always string. sounds like a number
            return 45 + heightCalc + 40 + 30
            //return 131.5
        }
        else{
            return 45 + heightCalc + 40
            //return 101.5
        }

    default: return 80.0
    }
}
person Lukas    schedule 15.12.2015
comment
причина проверки того, является ли indexPath.row - 3 ›= 0, заключается в том, что первые 3 ячейки не поступают из массива - person user2363025; 15.12.2015
comment
Хорошо, но это единственное место, где код может сломаться: let commentsDetailed = self.commentsDetailed![indexPath.row-3]. Я бы поместил это внутрь: - person Lukas; 15.12.2015
comment
спасибо за ответ, но если бы проблема действительно была в массиве за пределами границ, это происходило бы постоянно, а не для одного пользователя, или я что-то упустил? - person user2363025; 15.12.2015
comment
Я обновил код в соответствии с предложенной проверкой нулей, а также изменил свою проверку, если indexPath.row › 3, а не ›= 3. Пользователь по-прежнему получает периодические сбои. - person user2363025; 18.12.2015

в вашем коде все еще есть небольшая несогласованность. попытайтесь различать self.comentsDetailed и comentsDetailed в своем коде. попробуйте выбрать другое имя для локального значения let cd = self.commentsDetailed![indexPath.row-3] для примера, а затем используйте это значение там, где оно необходимо в следующих операторах. это может помочь вам увидеть, что не так в вашем коде.

person user3441734    schedule 15.12.2015