Получение данных с использованием dispatch_sync в UITabViewController

Я пишу приложение, которое использует UITabBarController для переключения представлений. Одна вкладка отправляет веб-запрос на сбор и обработку данных JSON перед заполнением UITableView. Я пытаюсь загрузить эти данные в фоновом режиме, поэтому, когда пользователь нажимает вкладку, представление таблицы не задерживается. Отобразится индикатор ActivityIndicator, а затем таблица будет загружена данными.

Каждый запрос делается, обрабатывается и результаты помещаются в NSMutableArray, который затем сортируется и добавляется в UITableView.

Когда я использую dispatch_sync, данные загружаются, а массив создается и отображается нормально, однако загрузка пользовательского интерфейса для представления блокируется. Я думаю, потому что по какой-то причине я не получаю этот запрос в фоновую очередь. Если я использую dispatch_async, я получаю исключения при попытке доступа к NSMutableArray в основном потоке.

Итак, мой вопрос заключается в том, что является правильным шаблоном, позволяющим пользователю переключаться на вкладку, содержащую этот TableView, представлять ActivityIndicator во время загрузки и обработки данных (в фоновом режиме), а затем после завершения UITableView загружается с обработанным данные.

Код Relavent из UITableViewController:

#define kBgQueue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)

- (void)viewDidLoad
{
    [super viewDidLoad];

    AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];

    currentLat = appDelegate.currentLatitude;
    currentLong = appDelegate.currentLongitude;

    finalDistanceArray = [[NSMutableArray alloc] init];

    [self compareLocationMMC];
    [self compareLocationLVMC];
    [self compareLocationSBCH];
    [self compareLocationGVCH];
    [self compareLocationSYVCH];

    NSSortDescriptor *lowestToHighest = [NSSortDescriptor sortDescriptorWithKey:@"distance" ascending:YES];

    [hospitalList sortUsingDescriptors:[NSArray arrayWithObject:lowestToHighest]];  

}

- (void)compareLocationMMC {


NSString * searchURL = [NSString stringWithFormat:@"http://maps.googleapis.com/maps/api/directions/json?origin=%f,%f&destination=%f,%f&sensor=true", currentLat.floatValue, currentLong.floatValue, MMC_LAT, MMC_LON];
NSURL * myURL = [NSURL URLWithString:searchURL];

dispatch_sync(kBgQueue, ^{
    NSData* data = [NSData dataWithContentsOfURL: 
                    myURL];
     [self performSelectorOnMainThread:@selector(fetchedData:) 
                          withObject:data waitUntilDone:YES];
});


}
//The compareLocationXXX method is repeated 4 more times with different search strings

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

//parse out the json data
NSError* error;
NSDictionary* json = [NSJSONSerialization 
                      JSONObjectWithData:responseData 

                      options:kNilOptions 
                      error:&error];

NSArray* stationDistance = [json objectForKey:@"routes"]; 

NSDictionary* legs = [stationDistance objectAtIndex:0];

NSArray* legsBetween = [legs objectForKey:@"legs"];

NSDictionary* distanceBetween = [legsBetween objectAtIndex:0];

finalDistance = [distanceBetween valueForKeyPath:@"distance.text"];

[finalDistanceArray addObject:finalDistance];

}

person blueHula    schedule 14.02.2012    source источник


Ответы (1)


Dispatch_sync() фактически заблокирует поток, из которого он вызывается, поскольку он останавливает выполнение, чтобы поставить блок в очередь. Dispatch_async — гораздо лучший выбор, поскольку он позволяет возобновить работу вызывающей функции. Вы можете обернуть один вызов внутри другого, чтобы позволить вам выполнять код завершения в основном потоке. Это также делает выполнение кодов чрезвычайно легко читаемым.

// Turn on status bar spinner, for example
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
    // Do background stuff here
        ....
    NSData* data = [NSData dataWithContentsOfURL:myURL];
    // used parsed data to populate data structure of some type
        ....
    dispatch_async(dispatch_get_main_queue(), ^{
        // Use background stuff here
        [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
    });
});

Хотя API на основе обратного вызова, такой как NSURLConnection, вероятно, лучше всего подходит для сетевой загрузки, это будет работать довольно хорошо. Особенно для мелких запросов.

person NJones    schedule 14.02.2012
comment
Спасибо! Я считаю, что это нормально работает в фоновом режиме, но последующий код обращается к NSMutableArray и делает это до загрузки данных, вызывая исключение. Как мне по существу дождаться загрузки и сортировки данных перед доступом к ним? - person blueHula; 15.02.2012
comment
Вы не должны использовать загружаемые данные до тех пор, пока не появится часть «Использовать фоновые материалы здесь». Скорее всего, это копирование извлеченных вами данных и вызов reloadTable Этот ответ, который я опубликовал, относится к вашей новой проблеме. - person NJones; 16.02.2012