W mojej klasie mam mnóstwo powtarzającego się kodu, który wygląda następująco:
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request
delegate:self];
Problem z żądaniami asynchronicznymi polega na tym, że gdy masz różne żądania i masz przypisanego delegata, który ma traktować je wszystkie jako jedną całość, wiele rozgałęzionych i brzydkich kodów zaczyna formułować:
Jakie dane odzyskujemy? Jeśli to zawiera, zrób to, w przeciwnym razie zrób inne. Myślę, że przydałaby się możliwość tagowania tych asynchronicznych żądań, tak jakbyś mógł oznaczać widoki identyfikatorami.
Byłem ciekawy, jaka strategia jest najbardziej efektywna w zarządzaniu klasą, która obsługuje wiele asynchronicznych żądań.
źródło
[NSMapTable weakToStrongObjectsMapTable]
zamiastCFMutableDictionaryRef
ai zaoszczędzić kłopotów. Pracował dobrze dla mnie.Mam projekt, w którym mam dwa różne NSURLConnections i chciałem użyć tego samego delegata. Stworzyłem dwie właściwości w mojej klasie, po jednej dla każdego połączenia. Następnie w metodzie delegata sprawdzam, czy to połączenie
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { if (connection == self.savingConnection) { [self.savingReturnedData appendData:data]; } else { [self.sharingReturnedData appendData:data]; } }
Pozwala mi to również w razie potrzeby anulować określone połączenie według nazwy.
źródło
Podklasa NSURLConnection do przechowywania danych jest czysta, zawiera mniej kodu niż niektóre inne odpowiedzi, jest bardziej elastyczna i wymaga mniej uwagi na temat zarządzania referencjami.
// DataURLConnection.h #import <Foundation/Foundation.h> @interface DataURLConnection : NSURLConnection @property(nonatomic, strong) NSMutableData *data; @end // DataURLConnection.m #import "DataURLConnection.h" @implementation DataURLConnection @synthesize data; @end
Używaj go tak, jak NSURLConnection i zbieraj dane w jego właściwości data:
- (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]; }
Otóż to.
Jeśli chcesz pójść dalej, możesz dodać blok, który będzie służył jako wywołanie zwrotne za pomocą kilku dodatkowych linii kodu:
// Add to DataURLConnection.h/.m @property(nonatomic, copy) void (^onComplete)();
Ustaw to tak:
DataURLConnection *con = [[DataURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO]; con.onComplete = ^{ [self myMethod:con]; }; [con start];
i wywołaj go po zakończeniu ładowania w następujący sposób:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection { ((DataURLConnection *)connection).onComplete(); }
Możesz rozszerzyć blok, aby akceptował parametry lub po prostu przekazać DataURLConnection jako argument do metody, która go potrzebuje w bloku no-args, jak pokazano
źródło
TO NIE JEST NOWA ODPOWIEDŹ. POKAŻ, JAK CIĘ ZROBIŁEM
Aby rozróżnić różne NSURLConnection w ramach metod delegatów tej samej klasy, używam NSMutableDictionary, aby ustawić i usunąć NSURLConnection, używając jego
(NSString *)description
jako klucza.Obiekt, który wybrałem,
setObject:forKey
to unikalny adres URL używany do inicjowaniaNSURLRequest
rozszerzeniaNSURLConnection
zastosowań.Po ustawieniu NSURLConnection jest oceniane pod adresem
-(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]];
źródło
Jedną z metod, które zastosowałem, jest nie używanie tego samego obiektu jako delegata dla każdego połączenia. Zamiast tego tworzę nowe wystąpienie mojej klasy analizy dla każdego połączenia, które jest wyłączane i ustawiam delegata na to wystąpienie.
źródło
Wypróbuj moją niestandardową klasę MultipleDownload , która obsługuje wszystkie te elementy.
źródło
Zwykle tworzę szereg słowników. Każdy słownik zawiera trochę informacji identyfikujących, obiekt NSMutableData do przechowywania odpowiedzi oraz samo połączenie. Po uruchomieniu metody delegata połączenia wyszukuję słownik połączenia i odpowiednio go obsługuję.
źródło
Jedną z opcji jest samodzielne utworzenie podklasy NSURLConnection i dodanie -tag lub podobnej metody. Konstrukcja NSURLConnection to celowo bardzo gołe kości, więc jest to całkowicie akceptowalne.
A może możesz utworzyć klasę MyURLConnectionController, która jest odpowiedzialna za tworzenie i zbieranie danych połączenia. Musiałby wtedy tylko poinformować twój główny obiekt kontrolera po zakończeniu ładowania.
źródło
w iOS5 i nowszych możesz po prostu użyć metody class
sendAsynchronousRequest:queue:completionHandler:
Nie ma potrzeby śledzenia połączeń, ponieważ odpowiedź jest zwracana w module obsługi zakończenia.
źródło
Lubię ASIHTTPRequest .
źródło
Jak wskazały inne odpowiedzi, powinieneś gdzieś przechowywać connectionInfo i wyszukiwać je po połączeniu.
Najbardziej naturalnym typem danych jest to
NSMutableDictionary
, ale nie można go zaakceptowaćNSURLConnection
jako kluczy, ponieważ połączeń nie można skopiować.Inną opcją użycia
NSURLConnections
jako kluczy wNSMutableDictionary
jest użycieNSValue valueWithNonretainedObject]
:NSMutableDictionary* dict = [NSMutableDictionary dictionary]; NSValue *key = [NSValue valueWithNonretainedObject:aConnection] /* store: */ [dict setObject:connInfo forKey:key]; /* lookup: */ [dict objectForKey:key];
źródło
Postanowiłem podklasę NSURLConnection i dodać tag, delegata i NSMutabaleData. Mam klasę DataController, która obsługuje całe zarządzanie danymi, w tym żądania. Stworzyłem protokół DataControllerDelegate, aby poszczególne widoki / obiekty mogły nasłuchiwać DataController, aby dowiedzieć się, kiedy ich żądania zostały zakończone, a jeśli to konieczne, ile zostało pobranych lub błędy. Klasa DataController może użyć podklasy NSURLConnection do uruchomienia nowego żądania i zapisać delegata, który chce nasłuchiwać DataController, aby wiedzieć, kiedy żądanie zakończyło się. To jest moje działające rozwiązanie w XCode 4.5.2 i iOS 6.
Plik DataController.h, który deklaruje protokół DataControllerDelegate). DataController jest również singletonem:
@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 <NSObject> -(void)dataFailedtoLoadWithMessage:(NSString *)message; -(void)dataFinishedLoading; @end
Kluczowe metody w pliku 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]]; }
Aby złożyć wniosek:
[[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 <DataControllerDelegate> dataDelegate; @property (strong, nonatomic) NSMutableData *receivedData; -(id)initWithRequest:(NSURLRequest *)request delegate:(id)delegate startImmediately:(BOOL)startImmediately tag:(NSString *)tag dataDelegate:(id)dataDelegate; @end
Oraz 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
źródło
Każdy NSURLConnection ma atrybut skrótu, możesz rozróżniać wszystko według tego atrybutu.
Na przykład muszę zachować pewne informacje przed i po połączeniu, więc mój RequestManager ma NSMutableDictionary, aby to zrobić.
Przykład:
// 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];
Na życzenie:
- (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]; }
źródło