0
votes

I have an app that has a tableviewcontroller with this viewDidLoad:

- (void)viewDidLoad{
    [super viewDidLoad];
    // begin animating the spinner
    [self.spinner startAnimating];

    [SantiappsHelper fetchUsersWithCompletionHandler:^(NSArray *users) {
        self.usersArray = [NSMutableArray array];
        for (NSDictionary *userDict in users) {
            [self.usersArray addObject:[userDict objectForKey:@"username"]];
        }
        //Reload tableview
        [self.tableView reloadData];
    }];
}

The Helper Class method is this:

+(void)fetchUsersWithCompletionHandler:(Handler)handler {

    NSString *urlString = [NSString stringWithFormat:@"http://www.myserver.com/myApp/fetchusers.php"];
    NSURL *url = [NSURL URLWithString:urlString];

    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:10];

    [request setHTTPMethod: @"GET"];

    __block NSArray *usersArray = [[NSArray alloc] init];

    //A
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
        // Peform the request
        NSURLResponse *response;
        NSError *error = nil;
        NSData *receivedData = [NSURLConnection sendSynchronousRequest:request
                                                     returningResponse:&response
                                                                 error:&error];
        if (error) {
            // Deal with your error
            if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
                NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
                NSLog(@"HTTP Error: %d %@", httpResponse.statusCode, error);
                return;
            }
            NSLog(@"Error %@", error);
            return;
        }

        NSString *responseString = [[NSString alloc] initWithData:receivedData encoding:NSUTF8StringEncoding];

        usersArray = [NSJSONSerialization JSONObjectWithData:[responseString dataUsingEncoding:NSASCIIStringEncoding] options:0 error:nil];

        if (handler){
            dispatch_async(dispatch_get_main_queue(), ^{
            handler(usersArray);
            });
        }
    });
}

The above code was suggested to me and it makes sense from what I know about GCD. Everything runs on the main queue, but before it dispatches to a background queue before the NSURLConnection synchronous call. After it gets the data it fills the usersArray and should return it to the main queue. The usersArray is populated and when it tests for if handler, it moves to the dispatch_asynch(dispatch_get_main_queue () line. But when it returns to the main queue to process the array dictionaries, the NSArray *users is empty. The app crashes with this error:

* Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '* -[__NSArrayM insertObject:atIndex:]: object cannot be nil'

If I comment out the dispatch_async(dispatch_get_main_queue() code to look like this:

if (handler){
            //dispatch_async(dispatch_get_main_queue(), ^{
            handler(usersArray);
            //});
        }

It works fine...well kinda, its a little sluggish. Why is this failing?

1
From my experience passing mutable arrays across threads is always trouble. You can't make it as a normal NSArray and pass it to the handler block, if you can its the best way..egghese
usersArray in SantiappsHelper is an NSArray, its not mutable.marciokoko

1 Answers

1
votes

Replacing

dispatch_async(dispatch_get_main_queue(),

With:

dispatch_sync(dispatch_get_main_queue(),

REASON: dispatch_sync will wait for the block to complete before execution