Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

Sam77

macrumors newbie
Original poster
Aug 17, 2010
22
0
I've got some inconsistencies with the app UIEvents (Scrolling, Reloading) and backgroundthread work. Occasional "NSRangeException" is kinda annoying, though not life threatning, nevertheless, for the sake of good code,

Here is where the conflict is showing up. I occasionally keep getting "Index beyond bounds" error.

This specially occurs when:
I tap a button, that fills the tableView. I scroll down to the last row. While the tableView scrolls, I tap another button that reloads the tableView with Lesser data(rows) and thats where the error comes out! Sometimes it works, sometimes i get the following:

Here is the IBAction:
1- Sqlite querying via NSOperation.
2- After getting data in an array the following method is called in MainThread in AppDelegate

Code:
[self.mainController performSelectorOnMainThread:@selector(cacheArray:) withObject:dataArray waitUntilDone:NO];


Code:
- (void) cacheArray:(NSArray *)CurentArray{
	self.currMainArray = CurentArray;
        if(CurrentArray) [tableView reloadData];
}


Code:
 Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[NSMutableArray objectAtIndex:]: index 33 beyond bounds [0 .. 14]

Code:
(
	0   CoreFoundation                      0x36440303 __exceptionPreprocess + 114
	1   libobjc.A.dylib                     0x3523d4c4 objc_exception_throw + 40
	2   CoreFoundation                      0x363c1751 -[__NSArrayM objectAtIndex:] + 184
	3   MyAppN                             0x0000af2b -[MainViewController tableView:cellForRowAtIndexPath:] + 156
	4   UIKit                               0x32405250 -[UITableView(UITableViewInternal) _createPreparedCellForGlobalRow:withIndexPath:] + 652
	5   UIKit                               0x32404eb4 -[UITableView(UITableViewInternal) _createPreparedCellForGlobalRow:] + 52
	6   UIKit                               0x323cb488 -[UITableView(_UITableViewPrivate) _updateVisibleCellsNow:] + 1308
	7   UIKit                               0x323c8e48 -[UITableView layoutSubviews] + 208
	8   UIKit                               0x32370ab8 -[UIView(CALayerDelegate) _layoutSublayersOfLayer:] + 40
	9   CoreFoundation                      0x363c85c1 -[NSObject(NSObject) performSelector:withObject:] + 24
	10  QuartzCore                          0x30c8c624 -[CALayer layoutSublayers] + 184
	11  QuartzCore                          0x30c8c2ac CALayerLayoutIfNeeded + 200
	12  QuartzCore                          0x30c8bbb8 _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 264
	13  QuartzCore                          0x30c8b7e0 _ZN2CA11Transaction6commitEv + 284
	14  QuartzCore                          0x30c939e0 _ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv + 88
	15  CoreFoundation                      0x3641424b __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 18
	16  CoreFoundation                      0x36415da5 __CFRunLoopDoObservers + 500
	17  CoreFoundation                      0x364172fd __CFRunLoopRun + 940
	18  CoreFoundation                      0x363be0c3 CFRunLoopRunSpecific + 226
	19  CoreFoundation                      0x363bdfd1 CFRunLoopRunInMode + 60
	20  GraphicsServices                    0x33c4cf90 GSEventRunModal + 196
	21  UIKit                               0x32363b48 -[UIApplication _run] + 572
	22  UIKit                               0x32361fc0 UIApplicationMain + 972
	23  MyAppN                             0x00002e39 main + 80
	24  MyAppN                             0x00002db0 start + 52
)

Particularly "updateVisibleCells" gives me a clue to the problem, but im not sure what exactly it is.

Code:
	6   UIKit                               0x323cb488 -[UITableView(_UITableViewPrivate) _updateVisibleCellsNow:] + 1308

Im not sure what do I do here. This error is eating my brain! hellppp
 
Yes, I updated tableView in the main thread.


Code:
[self.mainController performSelectorOnMainThread:@selector(cacheArray:) withObject:dataArray waitUntilDone:NO];

//in MainController
- (void) cacheArray:(NSArray *)CurentArray{
	self.currMainArray = CurentArray;
        if(CurrentArray) 	[tableView performSelectorOnMainThread:@selector(reloadData)  withObject:nil waitUntilDone:YES];
}

The same error continues. SIGABRT NSRangeException.
with similar description above.

Does the UIEvents that lead upto the error give clues??
A tableView with More than 30 rows of data when scrolled down, while the tableView is scrolling (in motion), tapping another button with LESS data (translating to less Rows) causes this error.
 
Well for whatever reason you are trying to access an object at index 33 of an array that only contains 15 objects.

As you have only posted tiny snippets of code we can't really say much more than that. The crash is happening in tableView:cellForRowAtIndexPath: which you have not posted. I would suggest checking that method and making sure you are not using a stale array reference.
 
Also, this error only occurs when tableView is scrolling.

Before error, the TableView has 35 rows.
The new array now has 14 rows.

When the tableView is not scrolling, theres no problem. Everything works smoothly.

I deduced that this is the problem only due to scrolling.

During scroll if tableView crosses more than 15 rows, i guess this error pops up cuz the new array that just came in(from the background thread) has only 14 rows worth data.

Maybe the trick is to stop scrolling?? I'll try this out.

thanks for your replies robbieduncan. Im hoping theres nothing wrong with cellForRowAtIndexPath. Cuz these events work fine when tableView is not scrolling. Any suggestions?
 
If you are in the middle of a scroll event when you call reload data does the table view actually reload the data? Or does it wait until the scroll completes? Put a NSLog in the method that returns the number of rows. I would bet that it does not get called until the scroll completes which is why the table is trying to read data for 25 rows. But you have replaced your array with one with only 14 rows so it crashes.
 
Yes, you are right.

Heres the Log:

Code:
                                                            Check array count=34 index=17
2010-09-14 19:02:44.887 MyAppN[1657:307] Check array count=34 index=18
2010-09-14 19:02:45.022 MyAppN[1657:6013] new ArrayCount= 15
2010-09-14 19:02:45.210 MyAppN[1657:307] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[NSMutableArray objectAtIndex:]: index 19 beyond bounds [0 .. 14]'

Its pretty clear now. While scrolling through an array with 34 count, thru cell index 17-18-... and SuDDENLY, there is a new array 15!! How in the world could the reloaded Tableview scrolll to Index 19!

seems like we figured it out! thanks!

For the solution though, im trying to stop the scrolling table and reload it. Atleast scroll it to the top. Yet I see no results almost the same error.

Code:
- (void) cacheArray:(NSArray *)CurentArray{
	//NSLog(@"im in cachearr");
	if (CurentArray) 
		
	{ 
		[tableView scrollRectToVisible:CGRectMake(0.0, 0.0, 1.0, 1.0) animated:NO]; 
 //After scrolling to the top,
		self.currMainArray = CurentArray;
		[tableView performSelectorOnMainThread:@selector(reloadData)  withObject:nil waitUntilDone:YES];
	}


Little improvement, the error continues!
 
The array that you are passing from the background thread to the main thread: is this a new array that was created in the background thread or is it an old array that comes from the object table view controller?

Why do you have a performSelectorOnMainThread in your cacheArray: method? Isn't this method already performed on the main thread? If this method is really running on the main thread this may be the cause of the problem. Call reloadData directly.
 
The array that you are passing from the background thread to the main thread: is this a new array that was created in the background thread or is it an old array that comes from the object table view controller?

This array is in appDelegate. Not from the tableView.

The tableView data source is within its ViewController (mainViewController). And the data is passed via cacheArray: (in mainViewController).

Why do you have a performSelectorOnMainThread in your cacheArray: method? Isn't this method already performed on the main thread? If this method is really running on the main thread this may be the cause of the problem. Call reloadData directly.

Actually I used reloadData directly. If you saw the code in the first post. I used reloadData.

I mayhav changed it to make that point that I'm doin it in main thread. my bad!
 
For the solution though, im trying to stop the scrolling table and reload it. Atleast scroll it to the top.
You shouldn't need to worry about stopping the scrolling or moving it to the top. That is barking up the wrong tree. Concentrate on getting the [tableView reloadData] to work properly.
 
You shouldn't need to worry about stopping the scrolling or moving it to the top. That is barking up the wrong tree. Concentrate on getting the [tableView reloadData] to work properly.

How exactly should I proceed? [tableView reloadData] does work properly, its only this index beyond bounds issue. And this occurs when things are going fast on the UI. Should I relook at the NSOperation procedure? that calls the cacheArray: on MainThread?

I really am without a clue here.
 
You can know when the scrolling operation is in progress and when it finishes. Defer the setting of the array and reload of the table until the scroll finishes.
 
I am dubious that calling reloadData when the table is scrolling is a problem. I have some code that does something very similar to what you're doing and I've never seen a problem or gotten a bug report about it. Mine is different in that it adds rows from a database asynchronously, while yours seems to be removing rows.

I've never heard of this problem before. If it was a general problem we would have heard of it.

Here's how this is supposed to work. On a background thread you create a new array and fill it with data. Then you call a method on the main thread. This method on the main thread updates the data model and calls reloadData. There can be no delay between updating the data model and calling reloadData.

I really doubt that scrolling while this is happening is a general problem.
 
I am dubious that calling reloadData when the table is scrolling is a problem. I have some code that does something very similar to what you're doing and I've never seen a problem or gotten a bug report about it. Mine is different in that it adds rows from a database asynchronously, while yours seems to be removing rows.

I've never heard of this problem before. If it was a general problem we would have heard of it.

Here's how this is supposed to work. On a background thread you create a new array and fill it with data. Then you call a method on the main thread. This method on the main thread updates the data model and calls reloadData. There can be no delay between updating the data model and calling reloadData.

I really doubt that scrolling while this is happening is a general problem.

Your right, this problem is kinda strange.Maybe if i make a sample project and see if this thing recurs. If it does I'll post it up here for someone merciful to look at.
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.