NSArray с несколькими вложенными запросами

#iphone #objective-c #nsurlconnection #nsurlrequest

#iPhone #objective-c #nsurlconnection #nsurlrequest

Вопрос:

У меня есть приложение, которое использует segmentedControl. Первый элемент — это элемент «Все», где остальные создаются из массива на основе результата веб-сервиса. Когда выбрано «Все», я хочу запросить весь запрос.

Как я могу это сделать,

 NSArray *urls = [NSArray arrayWithObjects:@"http://service/group/1/", 
                                          @"http://service/group/2/", nil];
  

Я хочу собрать все результаты вызовов в коллекцию и отобразить их в UITableView, когда выбран элемент «Все» и, вероятно, в viewDidLoad.
Для других сегментов выдается только один запрос и выполняется обратный вызов с массивом, который затем используется в:

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

Я попытался посмотреть на этот пример для выполнения запроса из массива MultipleDownloads

Спасибо,

Метод в моем ViewController для инициирования многократной загрузки:

 - (void)requestChildrenInBackground {

queue = [[NSOperationQueue alloc] init];

//Todo remove hard coded and get from previous request respons
NSArray *urls = [NSArray arrayWithObjects: @"http://service/1/children", 
                @"http://service/2/children",
                @"http://service/3/children", nil];

NSLog(@"%@", urls);    
for (NSString * url in urls)
{
    GetSchedule *operation =
    [GetSchedule urlDownloaderWithUrlString:url];
    [queue addOperation:operation];
}
}
  

Вот как обрабатывается множественный запрос:

 #import "GetSchedule.h"

#import "JSON.h"
#import "Authentication.h"
#import "AttendanceReportViewController.h"

@interface GetSchedule ()

- (void)finish;

@end

@implementation GetSchedule

@synthesize appDelegate;

@synthesize username;
@synthesize password;
@synthesize authenticationString;
@synthesize encodedLoginData;
@synthesize schedulesArray;

@synthesize url = _url;
@synthesize statusCode = _statusCode;
@synthesize data = _data;
@synthesize error = _error;
@synthesize isExecuting = _isExecuting;
@synthesize isFinished = _isFinished;

  (id)urlDownloaderWithUrlString:(NSString *)urlString {

NSURL * url = [NSURL URLWithString:urlString];
GetSchedule *operation = [[self alloc] initWithUrl:url];
return [operation autorelease];
}

- (id)initWithUrl:(NSURL *)url {

self = [super init];
if (self == nil)
    return nil;

_url = [url copy];
_isExecuting = NO;
_isFinished = NO;

return self;
}

- (void)dealloc
{
[username release];
[password release];
[encodedLoginData release];

[_url release];
[_connection release];
[_data release];
[_error release];
[super dealloc];
}

 - (BOOL)isConcurrent
{
return YES;
}

- (void)start
{
if (![NSThread isMainThread])
{
    [self performSelectorOnMainThread:@selector(start) withObject:nil waitUntilDone:NO];
    return;
}
self.username = appDelegate.username;
self.password = appDelegate.password;

Authentication *auth = [[Authentication alloc] init];
authenticationString = (NSMutableString*)[@"" stringByAppendingFormat:@"%@:%@", username, password];    
self.encodedLoginData = [auth encodedAuthentication:authenticationString];
[auth release];

NSLog(@"operation for <%@> started.", _url);

[self willChangeValueForKey:@"isExecuting"];
_isExecuting = YES;
[self didChangeValueForKey:@"isExecuting"];

// Setup up the request with the url
NSMutableURLRequest *request = [[NSMutableURLRequest alloc]
                                initWithURL:_url];

[request setHTTPMethod:@"GET"];
[request setValue:[NSString stringWithFormat:@"Basic %@", encodedLoginData] forHTTPHeaderField:@"Authorization"];

_connection = [[NSURLConnection alloc] initWithRequest:request
                                              delegate:self];
if (_connection == nil)
    [self finish];
else {
    _data = [[NSMutableData alloc] init];
}

 }

  - (void)finish
{
NSLog(@"operation for <%@> finished. "
      @"status code: %d, error: %@, data size: %u",
      _url, _statusCode, _error, [_data length]);

[_connection release];
_connection = nil;

[self willChangeValueForKey:@"isExecuting"];
[self willChangeValueForKey:@"isFinished"];

_isExecuting = NO;
_isFinished = YES;

[self didChangeValueForKey:@"isExecuting"];
[self didChangeValueForKey:@"isFinished"];
 }

#pragma mark -
#pragma mark NSURLConnection delegate

 - (void)connection:(NSURLConnection *)connection
didReceiveResponse:(NSURLResponse *)response
 {
//[_data release];
//_data = [[NSMutableData alloc] init];

[_data setLength:0];

NSHTTPURLResponse * httpResponse = (NSHTTPURLResponse *)response;
_statusCode = [httpResponse statusCode];
}

- (void)connection:(NSURLConnection *)connection
didReceiveData:(NSData *)data
{
[_data appendData:data];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// Parse the responseData of json objects retrieved from the service
SBJSON *parser = [[SBJSON alloc] init];

NSString *jsonString = [[NSString alloc] initWithData:_data encoding:NSUTF8StringEncoding];
NSDictionary *jsonData = [parser objectWithString:jsonString error:nil];
NSMutableArray *array = [jsonData objectForKey:@"Children"];

schedulesArray = [NSMutableArray array];
[schedulesArray addObject:array];

// Callback to AttendanceReportViewController that the responseData finished loading
[attendanceReportViewController loadSchedule];  

[self finish];

}

 - (void)connection:(NSURLConnection *)connection
 didFailWithError:(NSError *)error
 {
 _error = [error copy];
 [self finish];
 }

@end
  

Когда все данные получены, я хочу перезвонить моему ViewController и получить массив со всеми данными из всех сделанных запросов.

Комментарии:

1. Я рекомендую библиотеку ASIHTTPRequest , с ее помощью легко ставить в очередь несколько запросов. Перейдите на страницу и выберите, как ее использовать, затем выполните поиск, используя очередь .

2. @Nick ок, спасибо, но из этого я, вероятно, мог бы создать очередь без использования ASIHTTP. На самом деле у меня нет проблем с выполнением запросов относительно добавления результата в ту же коллекцию. Но я предполагаю, что я принял немного воды за голову, когда не использовал ASIHTTP с самого начала. Вероятно, мне нужно добавить полученные данные, а не создавать новый экземпляр NSData для каждого соединения.

3. Я просто предложил ASIHTTP, потому что это проще в использовании, чем NSURLConnections. Однако объединение результатов не должно быть проблемой, при вызове метода делегирования requestFinished просто используйте responseString, в конечном итоге проанализируйте и заполните свою модель.

4. @Nick Я опубликовал код som, чтобы сделать мой вопрос немного более понятным. Я хочу, чтобы по завершении всех запросов выполнялся обратный вызов моему ViewController, который затем может получить массив со всеми данными ответа.

5. Массив пуст, когда я выполняю обратный вызов для его извлечения. Но я не могу передать ViewController, как я делал раньше, методу класса GetSchedule.

Ответ №1:

Создайте загрузчик

  1. Создайте класс downloader, используя NSURLConnection.
  2. Сохраните переменную-член с именем downloaderObjectId.
  3. Напишите метод делегирования для этого объекта. Этот метод передаст загруженные данные и downloaderObjectId обратно делегату.

В делегате.

  1. Создайте несколько объектов downloader (в соответствии с вашей необходимостью) с уникальным значением для downloaderObjectId.

  2. Сохраните эти объекты в NSMutableDictionary

  3. Ключом для каждого объекта будет downloaderObjectId. чтобы при вызове метода делегирования после загрузки вы извлекали точный объект обратно из NSMutableDictionary, используя этот ключ.

Основной момент.

  1. Каждый раз вызывается делегат. Вы должны удалить объект из словаря (объект, который закончил загрузку и вызвал его delgate. Вы можете идентифицировать этот объект по ключу downloaderObjectId, который он содержит. )

  2. Затем проверьте количество словарей. Если оно равно нулю, вы можете убедиться, что ваши загрузки завершены. Таким образом, вы можете вызвать свой viewcontroller.