2016-12-03 3 views
1

Я хочу получить данные с сервера с несколькими вызовами внутри цикла. Каждый раз я передаю разные параметры. Я знаю, что можно получить данные, как, я выборки в коде ниже:iOS выборка данных из сервера внутри цикла

for (NSDictionary *feedItem in [feed objectForKey:@"content"]) { 
    // url with feedItem data. 
    NSURL *url = .... 
    [UrlMethod GetURL:url success:^(NSDictionary *placeData) { 
     if (placeData) { 
      dispatch_async(dispatch_get_main_queue(), ^{ 
       // adding object to table data source array 
       [dataSourceArray addObject:[placeData objectForKey:@"data"]]; 
       // reloading table view. 
       [self.tableView reloadData]; 

       }); 
      } 
     } failure:^(NSError *error) { 

     }]; 
} 

Проблема заключается в том, Всякий раз, когда я добавить данные в dataSourceArry, это не добавляет последовательно. Он добавляется в соответствии с ответом API-вызовов. Пожалуйста, дайте мне знать, если это не ясно.

+0

Есть много сообщений SO там, вы их проверяли? –

ответ

1

В вашем случае, я бы выделил изменяемый массив первого и установите [NSNull нуль] в каждой позиции:

NSInteger count = [[feed objectForKey:@"content"] count]; 
NSMutableArray *dataSourceArray = [NSMutableArray arrayWithCapacity:count]; 

for (NSInteger i = 0; i < count; ++i) { 
    [dataSourceArray addObject:[NSNull null]]; 
} 

Затем я хотел бы использовать то, что называется диспетчерские группы (подробнее здесь http://commandshift.co.uk/blog/2014/03/19/using-dispatch-groups-to-wait-for-multiple-web-services/):

__block NSError *apiCallError = nil; // Just to keep track if there was at least one API call error 
NSInteger index = 0; 

// Create the dispatch group 
dispatch_group_t serviceGroup = dispatch_group_create(); 

for (NSDictionary *feedItem in [feed objectForKey:@"content"]) { 

    // Start a new service call 
    dispatch_group_enter(serviceGroup); 

    // url with feedItem data. 
    NSURL *url = ... 

    [UrlMethod GetURL:url success:^(NSDictionary *placeData) { 
     if (placeData) { 
      dispatch_async(dispatch_get_main_queue(), ^{ 
       // Add data to Data Source 
       // index should be the correct one, as the completion block will contain a snapshot of the corresponding value of index 
       dataSourceArray[index] = [placeData objectForKey:@"data"]; 
      } 

      dispatch_group_leave(serviceGroup); 
     } failure:^(NSError *error) { 
      apiCallError = error; 
      dispatch_group_leave(serviceGroup); 
     }]; 

    index++; 
} 

dispatch_group_notify(serviceGroup, dispatch_get_main_queue(),^{ 
    if (apiCallError) { 
     // Make sure the Data Source contains no [NSNull null] anymore 
     [dataSourceArray removeObjectIdenticalTo:[NSNull null]]; 
    } 

    // Reload Table View 
    [self.tableView reloadData]; 
}); 

Надеюсь, он сработает для вас.

+0

Наконец-то кто-то написал более точный ответ :) Почувствовал грустный Я считал, что я отправлю код с помощью dispatch_group :) В любом случае отличная работа сэр :) Не уверен, что это будет принято в качестве ответа, но его 100% точно :) Perfect использование dispatch_group :) отсюда голосование :) –

+0

@SandeepBhandari Спасибо! – ppalancica

+0

Я бы, вероятно, использовал NSDictionary с номером слота в качестве ключа, потому что это позволяет вам проверить статус с помощью [dict count]. – dgatwood

1

Это может быть полезным для вас,

//keep dictionary property which will store responses 
    NSMutableDictionary *storeResponses = [[NSMutableDictionary alloc]init]; 

    //Somewhere outside function keep count or for loop 
    NSInteger count = 0; 

    for (NSDictionary *feedItem in [feed objectForKey:@"content"]) { 
     //Find out index of feddItem 
     NSInteger indexOfFeedItem = [[feed objectForKey:@"content"] indexOfObject:feedItem]; 

     NSString *keyToStoreResponse = [NSString stringWithFormat:@"%d",indexOfFeedItem]; 

     // url with feedItem data. 
     NSURL *url = .... 
     [UrlMethod GetURL:url success:^(NSDictionary *placeData) { 
      if (placeData) { 
       //instead of storing directly to array like below 
       // adding object to table data source array 
       [dataSourceArray addObject:[placeData objectForKey:@"data"]]; 

       //follow this 
       //increase count 
       count++; 
       [storeResponses setObject:[placeData objectForKey:@"data"] forKey:keyToStoreResponse]; 


       // reloading table view. 
       if(count == [feed objectForKey:@"content"].count) 
       { 
        NSMutableArray *keys = [[storeResponses allKeys] mutableCopy]; //or AllKeys 
        //sort this array using sort descriptor 
        //after sorting "keys" 

        for (NSString *key in keys) 
        { 
         //add them serially 

         [dataSourceArray addObject:[storeResponses objectForKey:key]]; 
        } 

        dispatch_async(dispatch_get_main_queue(), ^{ 


         [self.tableView reloadData]; 



        }); 
       } 

      } 
     } failure:^(NSError *error) { 

     }]; 
    } 

Edit: Ответ, который я дал прямо здесь написано, вы можете столкнуться с ошибками компиляции, а на самом деле работает этот код

+0

Пожалуйста, не просто отправляйте кучу кода без объяснений. Хороший ответ объясняет, в чем проблема, и объясняет, как ответ решает проблему. – rmaddy

+0

@rmaddy Спасибо за замечание, но дожди знают, в чем проблема, и я добавил много комментариев в код, чтобы помочь понять предлагаемое решение. Если вы все еще чувствуете необходимость редактировать ответ, вы можете продолжить и отредактировать мой ответ или дайте мне знать, что вы чувствуете, и я обновлю это. – sanman

0

Это происходит потому, вы асинхронно вызываете веб-сервисы, поэтому не гарантируете, что он дает ответ последовательно, как вы сделали запрос!

решения Теперь для этого:

  • Вы должны написать свой api как это дать все данные одновременно. Итак, Вам не нужно много звонить по сети, и это также улучшит производительность !
  • Во-вторых, вы можете сделать рекурсивный вид функции, я имею в виду сделать другой запрос от completion handler предыдущего. В этом случае, как только вы получите ответ, будет инициализирован только другой запрос, но в этом случае вам придется идти на компромисс с производительностью! Итак, первое решение лучше по мне!
  • Еще одна вещь, которую вы можете отсортировать массив после того, как вы получите все ответы, а затем вы можете перезагрузить tableView
+1

Я бы тоже предложил вам пойти с таким подходом. –

+0

yeah ....... :) @Dev_Tandel – Lion

0

Попробуйте следующее: -

for (NSDictionary *feedItem in [feed objectForKey:@"content"]) { 
     // url with feedItem data. 
     NSURL *url = .... 
     [UrlMethod GetURL:url success:^(NSDictionary *placeData) { 
      if (placeData) { 

        // adding object to table data source array 
        [dataSourceArray addObject:[placeData objectForKey:@"data"]]; 
        // reloading table view. 
dispatch_sync(dispatch_get_main_queue(), ^{ 
        [self.tableView reloadData]; 
    }); 
        }); 

      } failure:^(NSError *error) { 

      }]; 
    } 
+1

Операции пользовательского интерфейса всегда должны быть на главной теме. – ppalancica

+0

@ppalancica Спасибо! –

+0

Как этот ответ должен решить проблему? На самом деле это хуже. – rmaddy

0

Не загружайте таблицу каждый раз, когда в петля. После того, как цикл завершит выборку данных, выполните сортировку на вашем datasourcearray, чтобы получить желаемый результат, а затем перезагрузите таблицу.

Смежные вопросы