0

I have a problem with a UITableView. I load data from a database server in a background thread and if it is finished it sends a notification. In the notification I update the data array of my view and use reloadData on the tableview. Then the tableview deselects the selected row (that means the data is reloaded) and if I want to select another row I get EXC_BAD_ACCESS on the first line of didSelectRowAtIndexPath, even if it just is an NSLog.

If I don't assign the new array the background thread gives me to the variable data and don't use reloadData I have no problems with didSelectRowAtIndexPath but the tableview doesn't show the recent records. The user had to close the view and open it again to see the changes. That is really bad and I wanted to show the changes immediatly after the background thread finished to load the records from the server.

declared variables in the .h file:

-downloadThread is an NSThread,

-data is an NSArray,

-manager is my SQL interface.

-(void)viewDidLoad
{
    ...
    [super viewDidLoad];
    NSMutableArray *arr = [[manager getTeilnehmerList] retain];
    data = arr;
    [self.tableView reloadData];

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(KundenUpdated:) name:@"ContactUpdate" object:nil]; // to be notified when updating thread is finished

downloadThread = [[NSThread alloc] initWithTarget:self selector:@selector(teilnehmerLoad:) object:nil]; //Thread to get actual data on Background
[downloadThread performSelector:@selector(start) withObject:nil afterDelay:2];
    ...
}

-(void)teilnehmerLoad:(id)sender
{
    [manager loadTeilnehmerFromServerAndInsertIntoDatabase];
    //data = [manager getTeilnehmerList];
    [self.tableView reloadData];
    [[NSNotificationCenter defaultCenter] postNotificationName:@"ContactUpdate" object:nil];

}
-(void)KundenUpdated:(NSNotification*)notifaction
{
    @synchronized(self) 
    {
        //needs function to select row that was selected before reload if the data record is still there after sync with server
        [self.tableView reloadData];
        NSLog(@"count data in kundenupdated: %i",data.count);
    }
}
Wenduros
  • 13
  • 4
  • please post the code in the notification method ? – Midhun MP Nov 07 '12 at 13:00
  • So what are you logging in the first line of didSelectRowAtIndexPath? And what's happening in cellForRowAtIndexPath? I suspect that's where your bug is, possibly not recycling cells properly. – Hot Licks Nov 07 '12 at 13:02
  • Note that you should not call reloadData from background (this will definitely cause crashes), but should use peformSelectorOnMainThread or some such to cause the call to be executed in the UI thread. – Hot Licks Nov 07 '12 at 13:09
  • Why are you calling reloadData in both the method that sends the notification and the one that receives it? – Hot Licks Nov 07 '12 at 13:11
  • I was trying out which one will fit my needs. I removed reloadData from teilnehmerLoad: but there is still the same problem. – Wenduros Nov 07 '12 at 13:30

1 Answers1

0

I suspect that teilnehmerLoad is called in your background thread, and it's calling reloadData, which is a no-no.

Change it (and/or KundenUpdated) to use performSelectorOnMainThread to do the reloadData.

Make sure you're not doing any other UI operations from your background thread.

Hot Licks
  • 47,103
  • 17
  • 93
  • 151
  • in the background thread I start teilnehmerload: and if it is done it sends the notification "ContactUpdate" and the view has added itself as an observer of this notification and should start kundenUpdated: then. Is kundenUpdated: still in the background thread? I thought it is on the MainThread that's why assumed I could just reload the data. If I use performSelectorOnMainThread how should I build that ? I wrote [self performSelectorOnMainThread:(KundenUpdated) withObject:nil waitUntilDone:YES]; in teilnehmerLoad: now but I get a compiler error "Use of undeclared identifier 'KundenUpdated'" – Wenduros Nov 07 '12 at 13:39
  • ok the last problem was my mistake for reading to fast ;) I forgot the @selector(kundenUpdated:) – Wenduros Nov 07 '12 at 13:42
  • @Wenduros -- I'm a little weak on this topic, but I think that, by default, a notification runs in the notifying thread. There is a way, IIRC, to direct it to run on a different thread, though. – Hot Licks Nov 07 '12 at 14:09
  • I tried to call reloadData on the main thread with performSelectorOnMainThread and then I get a crash in cellForRowAtIndexpath. I also tried to use blocks in the viewDidLoad but reloadData then crashes again. – Wenduros Nov 07 '12 at 14:59
  • I'd suggest you figure out why you have the crash in cellForRowInIndexPath. You likely are recycling cells improperly or some such -- there are many opportunities for error there. – Hot Licks Nov 07 '12 at 17:15
  • I compared it to my other views and figured out that if I don't call any properties of the data array in cellForRowAtIndexPath I don't get errors. Does tableview reloadData not reload the cell creation or why do I get an exc_bad_access after reloading the tableview and using data.count or anything else of the array? – Wenduros Nov 08 '12 at 13:35
  • after I logged data.count in cellForRowAtIndexPath I get 0. The Log even only apears once.... so does cellForRowAtIndexPath not reload after reloadData ??? I thought it would be like this: view appears -> cellForRowAtIndex shows the current data count -> background thread finished -> reloadData -> cellForRowAtIndexPath shows new data count. – Wenduros Nov 08 '12 at 14:18
  • When you do reloadData, cellForRowAtIndexPath should be called once for each visible cell. Of course, numberOfSectionsInTableView and numberOfRowsInSection will be called first. If there are no sections or rows then cellForRowAtIndexPath will not be called. – Hot Licks Nov 08 '12 at 16:54