Управление несколькими асинхронными соединениями NSURLConnection

У меня есть тонна повторяющегося кода в моем classе, который выглядит следующим образом:

NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self]; 

Проблема с асинхронными запросами заключается в том, когда у вас разные запросы, и у вас есть делегат, назначенный для обработки их всех как одного объекта, много разветвляющихся и уродливых код начинает формулировать:

Какие данные мы возвращаемся? Если он содержит это, сделайте это, иначе сделайте другое. Было бы полезно, я думаю, иметь возможность отмечать эти асинхронные запросы, вроде как вы можете пометить представления с идентификаторами.

Мне было любопытно, какая страtagsя наиболее эффективна для управления classом, который обрабатывает несколько асинхронных запросов.

Я отслеживаю ответы в CFMutableDictionaryRef, связанный с NSURLConnection, связанным с ним. то есть:

 connectionToInfoMapping = CFDictionaryCreateMutable( kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 

Может показаться странным использовать это вместо NSMutableDictionary, но я это делаю, потому что этот CFDictionary сохраняет только свои ключи (NSURLConnection), тогда как NSDictionary копирует свои ключи (и NSURLConnection не поддерживает копирование).

Как только это будет сделано:

 CFDictionaryAddValue( connectionToInfoMapping, connection, [NSMutableDictionary dictionaryWithObject:[NSMutableData data] forKey:@"receivedData"]); 

и теперь у меня есть «информационный» словарь данных для каждого соединения, который я могу использовать для отслеживания информации о соединении, а словарь «информация» уже содержит изменяемый объект данных, который я могу использовать для хранения данных ответа по мере их поступления.

 - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { NSMutableDictionary *connectionInfo = CFDictionaryGetValue(connectionToInfoMapping, connection); [[connectionInfo objectForKey:@"receivedData"] appendData:data]; } 

У меня есть проект, в котором у меня есть два разных NSURLConnections, и я хотел использовать один и тот же делегат. Я создал два свойства в моем classе, по одному для каждого соединения. Затем в методе делегата я проверяю, какое соединение оно

 - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { if (connection == self.savingConnection) { [self.savingReturnedData appendData:data]; } else { [self.sharingReturnedData appendData:data]; } } 

Это также позволяет мне отменить определенное соединение по имени, когда это необходимо.

Подclass NSURLConnection для хранения данных чист, меньше кода, чем некоторые другие ответы, является более гибким и требует меньше внимания к управлению ссылками.

 // DataURLConnection.h #import  @interface DataURLConnection : NSURLConnection @property(nonatomic, strong) NSMutableData *data; @end // DataURLConnection.m #import "DataURLConnection.h" @implementation DataURLConnection @synthesize data; @end 

Используйте его, как и NSURLConnection, и накапливайте данные в свойстве данных:

 - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { ((DataURLConnection *)connection).data = [[NSMutableData alloc] init]; } - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { [((DataURLConnection *)connection).data appendData:data]; } 

Вот и все.

Если вы хотите пойти дальше, вы можете добавить блок, который будет служить обратным вызовом с помощью еще нескольких строк кода:

 // Add to DataURLConnection.h/.m @property(nonatomic, copy) void (^onComplete)(); 

Установите его так:

 DataURLConnection *con = [[DataURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO]; con.onComplete = ^{ [self myMethod:con]; }; [con start]; 

и вызывать его, когда загрузка завершена следующим образом:

 - (void)connectionDidFinishLoading:(NSURLConnection *)connection { ((DataURLConnection *)connection).onComplete(); } 

Вы можете расширить блок для принятия параметров или просто передать DataURLConnection в качестве аргумента для метода, который нуждается в нем в блоке no-args, как показано

ЭТО НЕ НОВЫЙ ОТВЕТ. ПОЖАЛУЙСТА, ПОЗВОЛЬТЕ, ЧТО Я ПОКАЗАЛ ВАС, КАК Я ДЕЛАТЬ

Чтобы различать разные NSURLConnection в рамках методов делегата одного classа, я использую NSMutableDictionary, чтобы установить и удалить NSURLConnection, используя его (NSString *)description качестве ключа.

Объект, который я выбрал для setObject:forKey – уникальный URL-адрес, который используется для NSURLRequest NSURLConnection используется NSURLConnection .

После установки NSURLConnection оценивается в

 -(void)connectionDidFinishLoading:(NSURLConnection *)connection, it can be removed from the dictionary. // This variable must be able to be referenced from - (void)connectionDidFinishLoading:(NSURLConnection *)connection NSMutableDictionary *connDictGET = [[NSMutableDictionary alloc] init]; //...// // You can use any object that can be referenced from - (void)connectionDidFinishLoading:(NSURLConnection *)connection [connDictGET setObject:anyObjectThatCanBeReferencedFrom forKey:[aConnectionInstanceJustInitiated description]]; //...// // At the delegate method, evaluate if the passed connection is the specific one which needs to be handled differently if ([[connDictGET objectForKey:[connection description]] isEqual:anyObjectThatCanBeReferencedFrom]) { // Do specific work for connection // } //...// // When the connection is no longer needed, use (NSString *)description as key to remove object [connDictGET removeObjectForKey:[connection description]]; 

Один из моих подходов – не использовать тот же объект, что и делегат для каждого соединения. Вместо этого я создаю новый экземпляр моего classа синтаксического анализа для каждого подключенного увольнения и назначает делегат этому экземпляру.

Попробуйте мой собственный class MultipleDownload , который обрабатывает все это для вас.

Я обычно создаю массив словарей. В каждом словаре есть немного идентификационной информации, объекта NSMutableData для хранения ответа и самого соединения. Когда запускается метод делегирования соединения, я просматриваю словарь соединения и обрабатываю его соответствующим образом.

Один из вариантов заключается только в подclassе NSURLConnection самостоятельно и добавлении -tag или аналогичного метода. Дизайн NSURLConnection преднамеренно очень голые кости, поэтому это вполне приемлемо.

Или, возможно, вы можете создать class MyURLConnectionController, который отвечает за создание и сбор данных соединения. После этого загрузка должна была бы только сообщить ваш основной объект controllerа.

в iOS5 и выше вы можете просто использовать метод classа sendAsynchronousRequest:queue:completionHandler:

Не нужно отслеживать соединения, так как ответ возвращается в обработчике завершения.

Мне нравится ASIHTTPRequest .

Как указывалось другими ответами, вы должны хранить connectionInfo где-то и искать их по соединению.

Наиболее естественным NSMutableDictionary данных для этого является NSMutableDictionary , но он не может принимать NSURLConnection как ключи, поскольку соединения не могут быть скопированы.

Другим вариантом использования NSURLConnections как ключей в NSMutableDictionary является использование NSValue valueWithNonretainedObject] :

 NSMutableDictionary* dict = [NSMutableDictionary dictionary]; NSValue *key = [NSValue valueWithNonretainedObject:aConnection] /* store: */ [dict setObject:connInfo forKey:key]; /* lookup: */ [dict objectForKey:key]; 

Я решил подclassифицировать NSURLConnection и добавить тег, делегат и NSMutabaleData. У меня есть class DataController, который обрабатывает все управление данными, включая запросы. Я создал протокол DataControllerDelegate, так что отдельные представления / объекты могут прослушивать DataController, чтобы узнать, когда их запросы были завершены, и при необходимости, сколько было загружено или ошибок. Класс DataController может использовать подclass NSURLConnection для запуска нового запроса и сохранять делегата, который хочет прослушать DataController, чтобы узнать, когда запрос завершен. Это мое рабочее решение в XCode 4.5.2 и ios 6.

Файл DataController.h, который объявляет протокол DataControllerDelegate). DataController также является одноэлементным:

 @interface DataController : NSObject @property (strong, nonatomic)NSManagedObjectContext *context; @property (strong, nonatomic)NSString *accessToken; +(DataController *)sharedDataController; -(void)generateAccessTokenWith:(NSString *)email password:(NSString *)password delegate:(id)delegate; @end @protocol DataControllerDelegate  -(void)dataFailedtoLoadWithMessage:(NSString *)message; -(void)dataFinishedLoading; @end 

Основные методы в файле DataController.m:

 -(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { NSURLConnectionWithDelegate *customConnection = (NSURLConnectionWithDelegate *)connection; NSLog(@"DidReceiveResponse from %@", customConnection.tag); [[customConnection receivedData] setLength:0]; } -(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { NSURLConnectionWithDelegate *customConnection = (NSURLConnectionWithDelegate *)connection; NSLog(@"DidReceiveData from %@", customConnection.tag); [customConnection.receivedData appendData:data]; } -(void)connectionDidFinishLoading:(NSURLConnection *)connection { NSURLConnectionWithDelegate *customConnection = (NSURLConnectionWithDelegate *)connection; NSLog(@"connectionDidFinishLoading from %@", customConnection.tag); NSLog(@"Data: %@", customConnection.receivedData); [customConnection.dataDelegate dataFinishedLoading]; } -(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { NSURLConnectionWithDelegate *customConnection = (NSURLConnectionWithDelegate *)connection; NSLog(@"DidFailWithError with %@", customConnection.tag); NSLog(@"Error: %@", [error localizedDescription]); [customConnection.dataDelegate dataFailedtoLoadWithMessage:[error localizedDescription]]; } 

И для начала запроса: [[NSURLConnectionWithDelegate alloc] initWithRequest:request delegate:self startImmediately:YES tag:@"Login" dataDelegate:delegate];

NSURLConnectionWithDelegate.h: @protocol DataControllerDelegate;

 @interface NSURLConnectionWithDelegate : NSURLConnection @property (strong, nonatomic) NSString *tag; @property id  dataDelegate; @property (strong, nonatomic) NSMutableData *receivedData; -(id)initWithRequest:(NSURLRequest *)request delegate:(id)delegate startImmediately:(BOOL)startImmediately tag:(NSString *)tag dataDelegate:(id)dataDelegate; @end 

И NSURLConnectionWithDelegate.m:

 #import "NSURLConnectionWithDelegate.h" @implementation NSURLConnectionWithDelegate -(id)initWithRequest:(NSURLRequest *)request delegate:(id)delegate startImmediately:(BOOL)startImmediately tag:(NSString *)tag dataDelegate:(id)dataDelegate { self = [super initWithRequest:request delegate:delegate startImmediately:startImmediately]; if (self) { self.tag = tag; self.dataDelegate = dataDelegate; self.receivedData = [[NSMutableData alloc] init]; } return self; } @end 

Каждый NSURLConnection имеет хеш-атрибут, вы можете различать все по этому атрибуту.

Например, мне нужно передать определенную информацию до и после подключения, поэтому для моего RequestManager требуется NSMutableDictionary.

Пример:

 // Make Request NSURLRequest *request = [NSURLRequest requestWithURL:url]; NSURLConnection *c = [[NSURLConnection alloc] initWithRequest:request delegate:self]; // Append Stuffs NSMutableDictionary *myStuff = [[NSMutableDictionary alloc] init]; [myStuff setObject:@"obj" forKey:@"key"]; NSNumber *connectionKey = [NSNumber numberWithInt:c.hash]; [connectionDatas setObject:myStuff forKey:connectionKey]; [c start]; 

После запроса:

 - (void)connectionDidFinishLoading:(NSURLConnection *)connection { NSLog(@"Received %d bytes of data",[responseData length]); NSNumber *connectionKey = [NSNumber numberWithInt:connection.hash]; NSMutableDictionary *myStuff = [[connectionDatas objectForKey:connectionKey]mutableCopy]; [connectionDatas removeObjectForKey:connectionKey]; } 
  • XCode 4 зависает при «Присоединении к (имя приложения)»
  • Установка UIDatePicker в таблицу UIActionSheet
  • iPhone: настройка заголовка панели навигации
  • Почему вы используете символ подчеркивания для переменной экземпляра, но не соответствующее ему свойство?
  • Проверка доступности iPhone
  • Как использовать первый символ в качестве имени раздела
  • UITextView с подсветкой синтаксиса
  • Работает ли UIGestureRecognizer на UIWebView?
  • Разбор даты JSON на IPhone
  • Ошибка: протокол недоступен, сброс backtrace
  • Как использовать метод prepareForReuse
  • Давайте будем гением компьютера.