heightForRowAtIndexPath crashing intermittently

2020-05-01 09:17发布

I have an app with about 30 active users and 500+ registered users and this issue is only happening with one person who is on 9.2 using an iPhone 6.

I can see from crash reports she's had over 60 crashes and she has said that the problem is OK sometimes and not others.

I have the same test device here and no issue is happening.

I can't get much out of the crash report details from fabric but the crash is happening at the following method and the app is crashing at the final else of this method, I can tell from her description of the problem:

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
            }

        }
    }

For those wondering, I need to calculate the height to avoid a jittery scroll which was happening when I was using the table view automatic dimension. So I just need to solve the issue within here and not switch approach.

Some info from 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 

And the heightForView func:

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
    }

Updated Code with checks for nil, still crashing for the user:

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
            }

        }
    }

3条回答
女痞
2楼-- · 2020-05-01 09:51

The error is array out of bounce at the last else closure like you said:

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

If for example the array is size 1, then the last element you can index is arr[0] but count >= (indexPath.row - 3) will let indexing arr[1] which will cause the error.

There are also other fixes you can do to optimize since this method is called repeatedly. (I'd suggest maybe having the fonts and other variables outside so that it doesn't create them each time. There maybe such optimization behind but I'm not sure).

I had to re-write the whole thing playground to find the problem so I guess I'll just paste the code here :)

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
    }
}
查看更多
女痞
3楼-- · 2020-05-01 09:52

there is still a little bit of inconsistency in your code. try to distinguish between self.comentsDetailed and comentsDetailed in you code. try to choose another name for the local value let cd = self.commentsDetailed![indexPath.row-3] for an example, and next use this value where it is necessary in next statements. this can help you to see what wrong happens in your code.

查看更多
闹够了就滚
4楼-- · 2020-05-01 09:56

I think the only possible point where the second else can fail is the commentsDetailed.comment! unwrapping, which should fail in case it were equal to nil. The rest seems perfectly robust, including the heightForView method. If checking commentsDetailed.comment != nil doesn't do the trick, I'll start thinking it's some random 9.2 early bug...

查看更多
登录 后发表回答