views:

69

answers:

1

Beginner iPhone programmer here, so bear with me.

Here's the problem: I have a UITableView. I also have a search controller. When the text in the search bar changes, it calls the reloadData method which adds getDataInBackground to the NSOperationQueue. Once the operation is done, it calls [tableView reloadData] in the main thread. Here's the code:

- (void)reloadData{
    if(activityLabel.superview == nil) //show activity label
        [self.view addSubview:activityLabel];

    NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(getDataInBackground) object:nil];
    [queue cancelAllOperations];
    [queue addOperation:operation];
    [operation release];
}

- (NSPredicate *)getPredicate{
    /* Sets up the appropriate predicate using search term and filters, returns it */
}

- (void)getDataInBackground{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    NSManagedObjectContext *context = [(FitAmp_LibraryAppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
    NSError *error;
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];

    NSEntityDescription *entity = [NSEntityDescription entityForName:@"ExerciseListInfo" inManagedObjectContext:context];
    [fetchRequest setEntity:entity];

    [fetchRequest setPredicate:[self getPredicate]];

    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"title" ascending:YES];   
    NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
    [fetchRequest setSortDescriptors:sortDescriptors];

    NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];

    NSMutableDictionary *tempExercises = [[NSMutableDictionary alloc] init];
    NSString *firstLetter;
    NSMutableArray *exercisesSection;
    for (ExerciseListInfo *exercise in fetchedObjects) {
        firstLetter = [[exercise.title substringToIndex:1] uppercaseString];
        if([[NSScanner scannerWithString:firstLetter] scanInteger:NULL])
            firstLetter = @"#";

        exercisesSection = [tempExercises objectForKey:firstLetter];
        if(exercisesSection == nil){
            NSMutableArray *newExerciseArray = [[NSMutableArray alloc] initWithObjects:exercise, nil];
            [tempExercises setObject:newExerciseArray forKey:firstLetter];
            [newExerciseArray release];
        }else{
            [exercisesSection addObject:exercise];
        }
        exercisesSection = nil;
    }

    NSMutableArray *tempSectionsArray = [[tempExercises allKeys] mutableCopy];

    self.exercisesSections = (NSMutableArray *)[tempSectionsArray sortedArrayUsingSelector:@selector(compare:)];
    self.exercises = tempExercises;

    [tempSectionsArray release];
    [tempExercises release];

    [self.tableView performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO];

    if(activityLabel.superview)
        [activityLabel performSelectorOnMainThread:@selector(removeFromSuperview) withObject:nil waitUntilDone:YES];

    [self performSelectorOnMainThread:@selector(printExercises) withObject:nil waitUntilDone:YES];

    [fetchRequest release]; 
    [sortDescriptor release];
    [sortDescriptors release];

    [pool drain];
}

The problem is that when the table reloads data, it appears to use the [self exercises] and [self exercisesSections] from the last time getDataInBackground ran. The value of the 2 objects are current and correct after every search (I print them out with NSLog) but tableView seems to be using an outdated copy.

What do I do?

EDIT: It seems like if I retain self.exercises and self.exercisesSections after I set them, the problem goes away. But then aren't I leaking memory?

A: 

Here is what I guess that can help you. Double check that the table view is reloaded.

You can put nslog or breakpoint inside the tableView:cellForRowAtIndexPath: to check for 2 things:

1/ the table view is actually reloaded.

2/ Check the value of excerises exactly at that point

vodkhang
Okay, did that.1. Yep, it's definitely being reloaded.2. Here's the problem. When I try to print out the contents of my exercises and exercisesSections using for..in loops, it crashes silently. That weird because I can print out the contents from the background thread and it works perfectly. Also, it only crashes when it's reloading from the *search*. When it reloads from filters that were modified, everything works fine. Thanks for your help.
kasrak
That's really weird because your problem is like this: it is updated on background thread, but in the main thread, it is not. And, if you want to print it out, it crashes. It makes no sense for me. I think if you print out and it crashes then when the table view try to set the cell, it should crash as well
vodkhang
Yea, it's driving me nuts. At least I'm pretty sure it's a memory handling problem now.
kasrak
It seems like if I retain self.exercises and self.exercisesSections after I set them, the problem goes away. But then aren't I leaking memory?
kasrak
then you should set @property (nonatomic, retain) and then release them in dealloc, it will solve the memory leak and memory crash. And when you set them, just do self.exerices = something, don't do retain anymore, you already set retain in property
vodkhang
They already are (nonatomic, retain) properties and they're released in dealloc. That's what I don't get...if I don't retain them in the background thread, it doesn't work.
kasrak
poor u, I believe that you encountered one of difficult situations of memory bug. I am trying to read your posted code in depth now:)
vodkhang
I can be quite sure that the code you posted have no problem. It should be somewhere else that you release/autorelease it.
vodkhang