Question

I'm trying to use a global NSMutableDictionary from a dispatch queue. However, the items keep coming back NULL.

What I'm trying to do is access an external json file with a dispatch_queue, then populate a UITableView with this info.

Here's what I have

vc.h:

@interface viewcontroller {
 NSMutableDictionary *jsonArray;
}

vc.m:

    #define kBgQueue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) //1
    #define jsonTest [NSURL URLWithString:@"http://www.sometest.com/test.php"]

    -(void)viewDidLoad {
      dispatch_async(kBgQueue, ^{
            NSData* data = [NSData dataWithContentsOfURL:
                            jsonTest];
           [self performSelectorOnMainThread:@selector(fetchedData:)
                                   withObject:data waitUntilDone:YES];
            // if I run the log here, I can access jsonArry and the log prints correctly
            NSLog(@"City: %@", [jsonArray objectForKey:@"city"];
        });
    }

    -(NSMutableDictionary *)fetchedData:(NSData *)responseData {

        NSError *error;
        jsonArray = [NSJSONSerialization JSONObjectWithData:responseData options:kNilOptions error:&error];
        return jsonArray;
    }

/********************* Table formatting area **********************/

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    if (tableView == self.ipTable) { 
        if ([ipArray count] == 0){
            return 1;
        } else { // meta table
            return [ipArray count];
        }
    } else { // IP Meta Data
        return [jsonArray count];
    }
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{


    if (tableView == self.myTable) {
        NSString *CellIdentifier = NULL;
        if ([ipArray count] == 0) {
            CellIdentifier = @"No Cells";
        } else {
            CellIdentifier = @"IP Cell";
        }

        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

        if (cell == nil) {
            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
        }

        if ([ipArray count] == 0)
        {
            [cell.textLabel setText:NSLocalizedString(@"None Found", nil)];
            return cell;

        } else {

        IPAddr *theip = [ipArray objectAtIndex: [indexPath row]];
        NSString *theipname = [theip ipName];
        if ([theipname isEqualToString:@""]) {
            [cell.textLabel setText: [theip ipNum]];
            [cell.detailTextLabel setText:NSLocalizedString(@"noName", nil)];
        } else {
            [cell.textLabel setText: [theip ipName]];
            [cell.detailTextLabel setText: [theip ipNum]];
        }
        return cell;
        }

    } else { // meta table

        static NSString *CellIdentifier = @"metaCell";

        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

        if (cell == nil) {
            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
        }

        // jsonArray content would go here to fill the cells.
        /******************** something here to fill the cells using jsonArray ********************/
        return cell;
    }

} // END UITAbleViewCell

If I access the jsonArray inside the queue, it returns fine and prints the log for the city. However, if I try to use it outside the queue, it returns NULL.

I'm trying to figure out what is happening, any ideas?

I need to use jsonArray in different methods in the same view, so I need it to be global.

Was it helpful?

Solution

I am fairly sure that the problem is that the data source methods (numberOfRowsInSection, cellForRowAtIndexPath) are called before the background thread has finished and filled the jsonArray. Therefore you have to reload the table view when the background thread has finished:

- (void)viewDidLoad
{
    [super viewDidLoad];

    dispatch_async(kBgQueue, ^{
        NSData *data = [NSData dataWithContentsOfURL:jsonTest];
        NSError *error;
        NSArray *tmpArray = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
        dispatch_sync(dispatch_get_main_queue(), ^{
            // Assign new data to data source and reload the table view:
            jsonArray = tmpArray;
            [self.metaTableView reloadData];
        });
    });
}

So the table view would be empty initially, and reloaded later when the data has arrived.

OTHER TIPS

Try to call the other method(which is using your jsonarray) through nsnotification...I am not sure there might some other ideas/ways of doing this.But i am presenting what i have in my mind.

Put this code inside your fetchedData method,

NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
            [nc addObserver:self selector:@selector(someMethod:) name:@"JSonDownloaded" object: jsonArray];
             [[NSNotificationCenter defaultCenter] postNotificationName:@"JSonDownloaded" object: jsonArray];

-(void)someMethod:(NSNotification *)nspk
{
    NSLog(@"%@",nspk.object);
//Only after this you can able to access the jsonArray.
}

Don't forget to unregister the observer.

jsonArray is just an instance variable, but not a property. Thus, assigning an object to it does not retain it, and the object may be released as soon as the program returns to the run loop.
I suggest replacing the iVar by @property (strong) NSMutableDictionary *jsonArray; and @synthesize jsonArray;, and assigning the object to it by self.jsonArray = ...
EDIT (see comment of Martin R below):
Thus, if you are not using ARC, assigning an object to it does not retain it, and the object may be released as soon as the program returns to the run loop.
In this case, I suggest replacing the iVar by @property (retain) NSMutableDictionary *jsonArray; and @synthesize jsonArray;, and assigning the object to it by self.jsonArray = ...

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top