2012-05-20 10 views
7

Ich mache eine JSON Anfrage mit AFNetworking und rufe dann [operation waitUntilFinished] auf, um auf die Operation und die Erfolgs- oder Fehlerblöcke zu warten. Aber, so scheint es richtig, wenn zu fallen - in Bezug auf den Log-Meldungen, erhalte ich "0", "3", "1" statt "0", "1", "3"Warten auf Fertigstellungsblock, um in einer AFNetworking Anfrage abzuschließen

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

Es scheint, als ob der Aufruf von '[operation waitUntilFinished]' nicht auf die Abschlussblöcke wartet. AFJSONRequestOperation.m führt sie mit 'dispatch_async' aus, was meiner Meinung nach Teil einer separaten Operation wird. Ist das richtig und gibt es einen Weg um es herum? – Kamran

Antwort

14

Dies funktioniert durch Verwenden von AFNetworking zum Einrichten der Anforderungen, aber zum Ausführen eines synchronen Aufrufs, bei dem die Abschlussblöcke manuell verarbeitet werden. Sehr einfach. AFNetworking scheint das https://github.com/AFNetworking/AFNetworking/wiki/AFNetworking-FAQ nicht zu unterstützen, obwohl die Arbeit einfach genug ist.

#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

That sollte (fast) funktionieren. Ihr Anruf zu

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

sollte wohl nicht [url path] zum path: Parameter übergeben. In AFNetworking Land ist dieser Pfad alles nach der Basis-URL (zum Beispiel könnte die Basis-URL "http://google.com" und der Pfad "/ gmail" oder was auch immer sein).

aber sagen, dass es wahrscheinlich keine gute Idee, den asynchronen Betrieb in einem Thread-blocking Synchronlauf mit waitUntilFinished zu machen, aber ich bin sicher, dass Sie Ihre Gründe haben ...;)

+0

Geändert zu Pfad: @ "/", aber das Ergebnis ist das gleiche. – Kamran

0

Ich hatte gerade das gleiche Problem und eine andere Lösung gefunden. Ich hatte zwei Operationen, die voneinander abhängen, aber parallel laden können. Der Vervollständigungsblock der zweiten Operation kann jedoch nicht ausgeführt werden, bevor der Vervollständigungsblock der ersten beendet ist.

Wie Colin darauf hingewiesen hat, könnte es eine schlechte Wahl sein, einen Web-Request-Block zu erstellen. Das war wichtig für mich, also habe ich es asynchron gemacht.

Das ist meine Lösung ist:

// 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

Warum verwenden Sie eine asynchrone Anfrage und zwingen Sie sie dann zu blockieren? Dies ist fehleranfällig, da Sie möglicherweise vom Rückruf zurückkommen, bevor Sie zur Entsperrungsanweisung gelangen. – Kamran

+0

Es ist nicht die Anfrage, die blockiert. Es ist die Lambda-Funktion, die aufgerufen wird, sobald die Anfrage beendet ist. Dies stellt sicher, dass die Antwort der zweiten (abhängigen) Anfrage nicht ausgewertet wird, bis die erste beendet ist. Die Anfragen selbst werden parallel ausgeführt. – Koraktor

0

Verwenden Delegate Methodenaufruf

die Methode in Block Put, die sich rufen beim Download/Upload abgeschlossen ist.