Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

findObjectsInBackgroundWithBlock: gets data from Parse, but data only exists inside the block

I made the following test class to try out retrieving data from Parse:

-(void)retrieveDataFromParse
{
    PFQuery *query = [PFQuery queryWithClassName:@"TestObject"];

    [query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
        if(!error){
            for (PFObject *object in objects){
                NSString *nameFromObject = [NSString stringWithFormat:@"%@", [object objectForKey:@"Name"]];
                NSString *dateFromObject = [NSString stringWithFormat:@"%@", [object createdAt]];
                NSString *scoreFromObject = [NSString stringWithFormat:@"%@", [object objectForKey:@"Score"]];
                [self addNewScore:scoreFromObject andDate:dateFromObject forUserName:nameFromObject];
                NSLog(@"The dictionary is %@", self.scoreDictionary); //<-- here it works printing out the whole dictionary
            }
        } else {
            NSLog(@"Error: %@ %@", error, [error userInfo]);
        }
    }];
    NSLog(@"The dictionary is %@", self.scoreDictionary); //<- but after the block is called, here the dictionary is again empty...
}

Per the commented section inside the code, when I print self.scoreDictionary inside the code, it works out fine, and I see my entire dictionary as it incrementally gets filled. However, after the block ends, when I print the dictionary again, it is now empty. I double checked with the query API docs, but I still am unsure what I am doing incorrectly.

like image 437
daspianist Avatar asked Aug 02 '13 16:08

daspianist


1 Answers

The last NSLog(@"The dictionary is %@", self.scoreDictionary) statement does not actually execute after the block completes. It executes after the findObjectsInBackgroundWithBlock method returns. findObjectsInBackgroundWithBlock presumably runs something in a separate thread, and your block may not actually execute at all until some length of time after that last NSLog statement. Graphically, something like this is probably happening:

Thread 1 
--------
retriveDataFromParse called
invoke findObjectsInBackgroundWithBlock
findObjectsInBackgroundWithBlock queues up work on another thread
findObjectsInBackgroundWithBlock returns immediately      |
NSLog statement - self.scoreDictionary not yet updated    |
retriveDataFromParse returns                              |
.                                                         V
.                       Thread 2, starting X milliseconds later
.                       --------
.                       findObjectsInBackgroundWithBlock does some work
.                       your block is called
.                       for-loop in your block
.                       Now self.scoreDictionary has some data
.                       NSLog statement inside your block

You probably want to think about, what do you want to do with your scoreDictionary data after you have retrieved it? For example, do you want to update the UI, call some other method, etc.? You will want to do this inside your block, at which point you know the data has been successfully retrieved. For example, if you had a table view you wanted to reload, you could do this:

for (PFObject *object in objects){
    ....
}
dispatch_async(dispatch_get_main_queue(), ^{
    [self updateMyUserInterfaceOrSomething];
});

Note the dispatch_async - if the work you need to do after updating your data involves changing the UI, you'll want that to run on the main thread.

like image 186
Mike Mertsock Avatar answered Nov 04 '22 11:11

Mike Mertsock