2012-05-20 10 views
7

Wysyłam żądanie JSON z AFNetworking, a następnie wywołuję operację [operation waitUntilFinished], aby czekać na działanie i bloki sukcesu lub awarii. Ale, wydaje się spadać prawo choć - w zakresie komunikatów dziennika, mam "0", "3", "1" zamiast "0", "1", "3"Oczekiwanie na zakończenie bloku zakończenia w żądaniu AFNetworking

NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://google.com"]]; 
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url]; 
httpClient.parameterEncoding = AFFormURLParameterEncoding; 
NSDictionary *params = [NSDictionary dictionaryWithObjectsAndKeys:@"query", @"q", nil]; 
NSMutableURLRequest *request = [httpClient requestWithMethod:@"GET" path:[url path] parameters:params]; 
NSLog(@"0"); 
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *innerRequest, NSHTTPURLResponse *response, id JSON) { 
NSLog(@"1"); 
gotResponse = YES; 
} failure:^(NSURLRequest *innerRequest, NSHTTPURLResponse *response, NSError *error, id JSON) { 
    NSLog(@"2"); 
    gotResponse = YES; 
}]; 
NSLog(@"Starting request"); 
[operation start]; 
[operation waitUntilFinished]; 
NSLog(@"3"); 
+0

Wygląda na to, że wywołanie funkcji "[operacja waitUntilFinished]" nie czeka na bloki zakończenia. AFJSONRequestOperation.m wykonuje je za pomocą 'dispatch_async', która moim zdaniem staje się częścią oddzielnej operacji. Czy to jest poprawne i czy istnieje sposób obejścia tego? – Kamran

Odpowiedz

14

to działa przez za pomocą AFNetworking, aby skonfigurować żądania, ale wykonując wywołanie synchroniczne, a następnie ręcznie wykonując bloki zakończenia. Bardzo prosta. Wydaje się, że AFNetworking nie obsługuje tego https://github.com/AFNetworking/AFNetworking/wiki/AFNetworking-FAQ, ale obejście jest dość proste.

#import "SimpleClient.h" 

#import "AFHTTPClient.h" 
#import "AFJSONRequestOperation.h" 
#import "AFJSONUtilities.h" 

@implementation SimpleClient 

+ (void) makeRequestTo:(NSString *) urlStr 
     parameters:(NSDictionary *) params 
     successCallback:(void (^)(id jsonResponse)) successCallback 
     errorCallback:(void (^)(NSError * error, NSString *errorMsg)) errorCallback { 

    NSURLResponse *response = nil; 
    NSError *error = nil; 

    NSURL *url = [NSURL URLWithString:urlStr]; 

    AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url]; 

    httpClient.parameterEncoding = AFFormURLParameterEncoding; 

    NSMutableURLRequest *request = [httpClient requestWithMethod:@"POST" path:[url path] parameters:params]; 
    NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error]; 

    if(error) { 
     errorCallback(error, nil); 
    } else { 
     id JSON = AFJSONDecode(data, &error); 
     successCallback(JSON); 
    } 
} 

@end 
0

Że powinien (prawie) działać. Twoje wezwanie do

NSMutableURLRequest *request = [httpClient requestWithMethod:@"GET" path:[url path] parameters:params]; 

nie powinien prawdopodobnie przejdzie [url path] do parametru path:. W sieci AFNetworking ta ścieżka jest wszystkim po adresie URL bazy (na przykład adres podstawowy to "http://google.com" i ścieżka "/ gmail" lub cokolwiek innego).

Powiedział, że jest to chyba nie jest dobry pomysł, aby operacja asynchroniczna do pracy synchronicznej gwintem blokującym z waitUntilFinished, ale jestem pewien, że masz swoje powody ...;)

+0

Zmieniono na ścieżkę: @ "/", ale wynik jest taki sam. – Kamran

0

Po prostu miałem ten sam problem i znalazłem inne rozwiązanie. Miałem dwie operacje, które zależą od siebie, ale mogą być ładowane równolegle. Jednakże bloku zakończenia drugiej operacji nie można wykonać przed zakończeniem bloku pierwszego.

Jak zauważył Colin, wybór bloku żądań internetowych może być złym wyborem. Było to dla mnie bardzo ważne, więc zrobiłem to asynchronicznie.

To jest moje rozwiązanie:

// This is our lock 
@interface SomeController() { 
    NSLock *_dataLock; 
} 
@end 

@implementation 

// This is just an example, you might as well trigger both operations in separate 
// places if you get the locking right 
// This might be called e.g. in awakeFromNib 
- (void)someStartpoint { 
    AFJSONRequestOperation *operation1 = [AFJSONRequestOperation JSONRequestOperationWithRequest:[NSURLRequest requestWithURL:url1] 
                         success:^(NSURLRequest *request, NSHTTPURLResponse *response, id data) { 
     // We're done, we unlock so the next operation can continue its 
     // completion block 
     [_dataLock unlock]; 
    } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id data) { 
     // The request has failed, so we need to unlock for the next try 
     [_dataLock unlock]; 
    }]; 

    AFJSONRequestOperation *operation2 = [AFJSONRequestOperation JSONRequestOperationWithRequest:[NSURLRequest requestWithURL:url2] 
                         success:^(NSURLRequest *request, NSHTTPURLResponse *response, id data) { 
     // The completion block (or at least the blocking part must be run in a 
     // separate thread 
     [NSThread detachNewThreadSelector:@selector(completionBlockOfOperation2:) toTarget:self withObject:data]; 
    } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id data) { 
     // This second operation may fail without affecting the lock 
    }]; 

    // We need to lock before both operations are started 
    [_dataLock lock]; 

    // Order does not really matter here 
    [operation2 start]; 
    [operation1 start]; 
} 

- (void)completionBlockOfOperation2:(id)data { 
    // We wait for the first operation to finish its completion block 
    [_dataLock lock]; 

    // It's done, so we can continue 

    // We need to unlock afterwards, so a next call to one of the operations 
    // wouldn't deadlock 
    [_dataLock unlock]; 
} 

@end 
+0

Dlaczego warto użyć żądania asynchronizacji, a następnie wymusić blokowanie? Jest to podatne na błędy, ponieważ możesz powrócić z wywołania zwrotnego przed uzyskaniem instrukcji odblokowania. – Kamran

+0

To nie jest blokowanie żądania. Jest to funkcja lambda, która jest wywoływana po zakończeniu żądania. Zapewnia to, że odpowiedź drugiego (zależnego) żądania nie zostanie oceniona, dopóki pierwsza nie zostanie zakończona. Same wnioski są wykonywane równolegle. – Koraktor

0

Zastosowanie Delegat wywołanie metody

Put metodę wewnątrz bloku, który będzie nazywać się podczas pobierania/przesyłania kończy.

Powiązane problemy