PDA

View Full Version : [Resolved] How to prevent number of rows assertion failure?




ArtOfWarfare
Sep 13, 2012, 11:02 PM
So far, this is how my app works: the user types in a web address, and as they type, the app tries to scan pages for RSS feeds. To do this, quickly and without blocking the main thread, it creates a bunch of NSOperations running on their own threads, plus a Queue to manage them.

As each operation finishes scanning an RSS feed, it calls this method in my table view controller:

-(void)searchGotRSS:(NSDictionary*)dictionary {
[self.tableView beginUpdates];
[feeds addObject:dictionary];
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:[feeds count]-1 inSection:0]] withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];
[self promptUpdate];
}

feeds is an NSMutableArray iVar.
promptUpdate just updates the search prompt. (By using the prompt, I don't need to bother with inserting a cell that tells the user what's going on.)

My number of rows method is this:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [feeds count];
}

And I only have a single section, so the number of section method just returns 1 always.

My app works perfectly somewhere around 40% of the time, but I'd say 60% of the time it prints out something like this:
*** Assertion failure in -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit/UIKit-1914.84/UITableView.m:1037
2012-09-13 23:55:50.560 FeedReader[29052:8003] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (3) must be equal to the number of rows contained in that section before the update (1), plus or minus the number of rows inserted or deleted from that section (1 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'

It seems to me that my method should work all the time, because it always starts with a call to the tableView to beginUpdates, which locks it from being edited by other threads, correct?, it follows it up with the data being edited and the new row being inserted, and then it ends with an endUpdates. I don't see how it's possible for me to end up with a different count in my datasource vs. in my view? (If you need more code than I've provided, I'll be happy to share.)



dejo
Sep 14, 2012, 09:38 AM
All UI updates must be done on the main thread. Is that how searchGotRSS: is being handled?

ArtOfWarfare
Sep 14, 2012, 10:12 AM
All UI updates must be done on the main thread. Is that how searchGotRSS: is being handled?

!!!

The RSSSearchOperation (which is on its own thread) simply uses this:

[self.delegate searchGotRSS:feed];

I think of objects as being on threads, but I'm suddenly thinking that I'm incorrect about that. The fact that the delegate is on the main thread... isn't actually a fact at all?

Sometimes the code works perfectly though... but I guess that just means that sometimes I get lucky?

Now I'm wondering whether I should modify the invocation of searchGotRSS (so it gets called on the main thread) or if I should modify the inside of the method, so that it switches itself to the main thread (if that makes sense? This is the first time I've ever tried writing code on my own that uses threads... in the past I've either avoided it or just been following someone else's tutorial.)

dejo
Sep 14, 2012, 11:57 AM
I think of objects as being on threads, but I'm suddenly thinking that I'm incorrect about that. The fact that the delegate is on the main thread... isn't actually a fact at all?
Nope, it's not. Objects are not on threads. Methods are run within threads.

Sometimes the code works perfectly though... but I guess that just means that sometimes I get lucky?
Sounds like it to me.

Now I'm wondering whether I should modify the invocation of searchGotRSS (so it gets called on the main thread) or if I should modify the inside of the method, so that it switches itself to the main thread (if that makes sense? This is the first time I've ever tried writing code on my own that uses threads... in the past I've either avoided it or just been following someone else's tutorial.)
I would modify searchGotRSS: so that only the part that does need to update the UI gets done within the main thread. I'm pretty sure you can find an easy way to do this, too.

ArtOfWarfare
Sep 14, 2012, 11:58 PM
I would modify searchGotRSS: so that only the part that does need to update the UI gets done within the main thread. I'm pretty sure you can find an easy way to do this, too.

Thanks for the help :)

My fixed code is this:

-(void)searchGotRSS:(NSDictionary*)dictionary {
dispatch_async(dispatch_get_main_queue(),^ {
[self.tableView beginUpdates];
[feeds addObject:dictionary];
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:[feeds count]-1 inSection:0]] withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];
[self promptUpdate];
});
}

I figure just perform it all on the main thread... I don't think any of it is expensive and feeds needs to have the correct number of items in it. And other than adding the dictionary to my feeds array, the other methods are all UIUpdate related.