1

I have the following method that selects the row for the current center view controller:

- (void)_updateSelection
{
    [self.tableView reloadData];

    [_viewControllers enumerateKeysAndObjectsUsingBlock:^(NSString *path, UIViewController *viewController, BOOL *stop) {
        if (viewController == self.revealSideViewController.rootViewController) {
            NSInteger row = [self indexOfPath:path];
            if (row != NSNotFound && row < [self.tableView numberOfRowsInSection:0]) {
                [self.tableView selectRowAtIndexPath:[NSIndexPath indexPathForRow:row inSection:0] animated:NO scrollPosition:UITableViewScrollPositionNone];
            }

            *stop = YES;
        }
    }];
}

As you can see, I have several checks to make sure everything is valid. According to the table view, the row is within the bounds of the section. And most of the time, this works just fine. Because the table view keeps loosing its selection when reloadData is called, I call this when I need to make changes. I also call it in viewWillAppear:. Here is where it occasionally crashes.

The output of the crash is:

*** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 2 beyond bounds [0 .. 0]'
*** First throw call stack:
(
    0   CoreFoundation                      0x041415e4 __exceptionPreprocess + 180
    1   libobjc.A.dylib                     0x03ec48b6 objc_exception_throw + 44
    2   CoreFoundation                      0x040e24e6 -[__NSArrayM objectAtIndex:] + 246
    3   UIKit                               0x022aaa5c -[UITableView cellForRowAtIndexPath:] + 235
    4   UIKit                               0x022ad6f0 -[UITableView _selectRowAtIndexPath:animated:scrollPosition:notifyDelegate:] + 1320
    5   UIKit                               0x022ad983 -[UITableView selectRowAtIndexPath:animated:scrollPosition:] + 63
    6   The City                            0x000073a2 __40-[TCMainMenuController _updateSelection]_block_invoke + 578
    7   CoreFoundation                      0x041cb13a __65-[__NSDictionaryM enumerateKeysAndObjectsWithOptions:usingBlock:]_block_invoke + 106
    8   CoreFoundation                      0x041cb03e -[__NSDictionaryM enumerateKeysAndObjectsWithOptions:usingBlock:] + 190
    9   CoreFoundation                      0x0412f525 -[NSDictionary enumerateKeysAndObjectsUsingBlock:] + 53
    10  The City                            0x0000713c -[TCMainMenuController _updateSelection] + 220
    11  The City                            0x00006999 -[TCMainMenuController viewWillAppear:] + 105
    12  UIKit                               0x022e2bfa -[UIViewController _setViewAppearState:isAnimating:] + 419
    13  UIKit                               0x022e3108 -[UIViewController __viewWillAppear:] + 114
    14  UIKit                               0x022e40c7 -[UIViewController viewWillMoveToWindow:] + 387
    15  UIKit                               0x02227384 -[UIView(Hierarchy) _willMoveToWindow:withAncestorView:] + 619
    16  UIKit                               0x02232f60 -[UIView(Internal) _addSubview:positioned:relativeTo:] + 470
    17  UIKit                               0x022269ed -[UIView(Hierarchy) insertSubview:belowSubview:] + 55
    18  The City                            0x0034c6b4 -[PPRevealSideViewController pushViewController:onDirection:withOffset:animated:forceToPopPush:completion:] + 2260
    19  The City                            0x000afc9c -[TCRootViewController pushViewController:onDirection:withOffset:animated:forceToPopPush:completion:] + 428
    20  The City                            0x0034bc9c -[PPRevealSideViewController pushViewController:onDirection:withOffset:animated:completion:] + 236
    21  The City                            0x0034b8a4 -[PPRevealSideViewController pushViewController:onDirection:animated:completion:] + 212
    22  The City                            0x0034b7b5 -[PPRevealSideViewController pushViewController:onDirection:animated:] + 149
    23  The City                            0x000af832 -[TCRootViewController showMenu] + 162
    24  The City                            0x00009a13 +[App showMenu] + 83
    25  The City                            0x00008611 -[TCMainMenuController showMenu:] + 81
    26  libobjc.A.dylib                     0x03ed6874 -[NSObject performSelector:withObject:withObject:] + 77
    27  UIKit                               0x021cd0c2 -[UIApplication sendAction:to:from:forEvent:] + 108
    28  UIKit                               0x024a1c9b -[UIBarButtonItem(UIInternal) _sendAction:withEvent:] + 139
    29  libobjc.A.dylib                     0x03ed6874 -[NSObject performSelector:withObject:withObject:] + 77
    30  UIKit                               0x021cd0c2 -[UIApplication sendAction:to:from:forEvent:] + 108
    31  UIKit                               0x021cd04e -[UIApplication sendAction:toTarget:fromSender:forEvent:] + 61
    32  UIKit                               0x022c50c1 -[UIControl sendAction:to:forEvent:] + 66
    33  UIKit                               0x022c5484 -[UIControl _sendActionsForEvents:withEvent:] + 577
    34  UIKit                               0x022c4733 -[UIControl touchesEnded:withEvent:] + 641
    35  UIKit                               0x0220a51d -[UIWindow _sendTouchesForEvent:] + 852
    36  UIKit                               0x0220b184 -[UIWindow sendEvent:] + 1232
    37  UIKit                               0x021dee86 -[UIApplication sendEvent:] + 242
    38  UIKit                               0x021c918f _UIApplicationHandleEventQueue + 11421
    39  CoreFoundation                      0x040ca83f __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 15
    40  CoreFoundation                      0x040ca1cb __CFRunLoopDoSources0 + 235
    41  CoreFoundation                      0x040e729e __CFRunLoopRun + 910
    42  CoreFoundation                      0x040e6ac3 CFRunLoopRunSpecific + 467
    43  CoreFoundation                      0x040e68db CFRunLoopRunInMode + 123
    44  GraphicsServices                    0x04f8b9e2 GSEventRunModal + 192
    45  GraphicsServices                    0x04f8b809 GSEventRun + 104
    46  UIKit                               0x021cbd3b UIApplicationMain + 1225
    47  The City                            0x000026ad main + 141
    48  libdyld.dylib                       0x0468270d start + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException

Using the debugger, I can see that -visibleCells, while not empty, does not have the cell I am trying to select (but it should, other than it is offscreen).

David Beck
  • 10,099
  • 5
  • 51
  • 88

1 Answers1

0

Your issue stems from the fact that -reloadData is run when you return control back to the run loop. The way you're doing it right now will cause a crash because -reloadData isn't being performed until after you leave that block.

One way to fix this is to dispatch a block to be run on the next run loop:

[self.tableView reloadData];
dispatch_async(dispatch_get_main_thread(), ^{
    [_viewControllers enumerateKeysAndObjectsUsingBlock:^(NSString *path, UIViewController *viewController, BOOL *stop) {
        if (viewController == self.revealSideViewController.rootViewController) {
            NSInteger row = [self indexOfPath:path];
            if (row != NSNotFound && row < [self.tableView numberOfRowsInSection:0]) {
                [self.tableView selectRowAtIndexPath:[NSIndexPath indexPathForRow:row inSection:0] animated:NO scrollPosition:UITableViewScrollPositionNone];
            }

            *stop = YES;
        }
    }];
});

See this discussion: How to tell when UITableVIew has completed ReloadData?

Community
  • 1
  • 1
aust
  • 914
  • 4
  • 12
  • Not correct. reloadData is synchronous, whoever said it isn't is wrong and the link you provided clarifies that. – strangetimes Nov 15 '15 at 19:41
  • @strangetimes It's asynchronous. See http://stackoverflow.com/questions/9675667/is-uitableview-reloaddata-asynchronous-or-synchronous – aust Dec 14 '15 at 18:00