J'ai une fonction utilisant AFJSONRequestOperation, et je souhaite retourner le résultat seulement après le succès. Pourriez-vous me diriger dans la bonne direction? Je suis encore un peu désemparé avec les blocs et AFNetworking en particulier.
-(id)someFunction{
__block id data;
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request
success:^(NSURLRequest *request, NSHTTPURLResponse *response, id json){
data = json;
return data; // won't work
}
failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error){
}];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation: operation];
return data; // will return nil since the block doesn't "lock" the app.
}
Pour bloquer l'exécution du thread principal jusqu'à la fin de l'opération, vous pouvez faire [operation waitUntilFinished]
après l'ajout à la file d'attente des opérations. Dans ce cas, vous n'auriez pas besoin du return
dans le bloc; définition du __block
variable suffirait.
Cela dit, je déconseille fortement de forcer les opérations asynchrones aux méthodes synchrones. Il est parfois difficile de se mettre en tête, mais s'il existe un moyen de structurer cela de manière asynchrone, ce serait presque certainement la voie à suivre.
J'utilise des sémaphores pour résoudre ce problème. Ce code est implémenté dans ma propre classe héritée de AFHTTPClient
.
__block id result = nil;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
NSURLRequest *req = [self requestWithMethod:@"GET"
path:@"someURL"
parameters:nil];
AFHTTPRequestOperation *reqOp = [self HTTPRequestOperationWithRequest:req
success:^(AFHTTPRequestOperation *operation, id responseObject) {
result = responseObject;
dispatch_semaphore_signal(semaphore);
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
dispatch_semaphore_signal(semaphore);
}];
reqOp.failureCallbackQueue = queue;
reqOp.successCallbackQueue = queue;
[self enqueueHTTPRequestOperation:reqOp];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_release(semaphore);
return result;
Je suggérerais que vous ne fassiez pas une méthode synchrone avec AFNetworking (ou les blocs en général). Une bonne approche consiste à créer une autre méthode et à utiliser les données json du bloc de réussite comme argument.
- (void)methodUsingJsonFromSuccessBlock:(id)json {
// use the json
NSLog(@"json from the block : %@", json);
}
- (void)someFunction {
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request
success:^(NSURLRequest *request, NSHTTPURLResponse *response, id json){
// use the json not as return data, but pass it along to another method as an argument
[self methodUsingJsonFromSuccessBlock:json];
}
failure:nil];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation: operation];
}
Il convient de noter que certaines fonctionnalités de AFClient d'AFNetworking peuvent toujours être utilisées de manière synchrone, ce qui signifie que vous pouvez toujours utiliser des subtilités telles que les en-têtes d'autorisation et les téléchargements en plusieurs parties.
Par exemple:
NSURLRequest *request = [self.client requestWithMethod: @"GET"
path: @"endpoint"
parameters: @{}];
NSHTTPURLResponse *response = nil;
NSError *error = nil;
NSData *responseData = [NSURLConnection sendSynchronousRequest: request
returningResponse: &response
error: &error];
N'oubliez pas de vérifier response.statusCode
dans ce cas, car cette méthode ne considère pas les codes d'échec HTTP comme des erreurs.
Ajoutez ceci sous le code avec lequel vous travaillez normalement:
[operation start];
[operation waitUntilFinished];
// do what you want
// return what you want
Exemple:
+ (NSString*) runGetRequest:(NSString*)frontPath andMethod:(NSString*)method andKeys:(NSArray*)keys andValues:(NSArray*)values
{
NSString * pathway = [frontPath stringByAppendingString:method];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:pathway]];
NSMutableDictionary * params = [[NSMutableDictionary alloc] initWithObjects:values forKeys:keys];
NSMutableURLRequest *request = [httpClient requestWithMethod:@"GET"
path:pathway
parameters:params];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[httpClient registerHTTPOperationClass:[AFHTTPRequestOperation class]];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject)
{
// Success happened here so do what ever you need in a async manner
}
failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
//error occurred here in a async manner
}];
[operation start];
[operation waitUntilFinished];
// put synchronous code here
return [operation responseString];
}
Pour développer/mettre à jour la réponse de @ Kasik. Vous pouvez créer une catégorie sur AFNetworking comme si vous utilisiez des sémaphores:
@implementation AFHTTPSessionManager (AFNetworking)
- (id)sendSynchronousRequestWithBaseURLAsString:(NSString * _Nonnull)baseURL pathToData:(NSString * _Nonnull)path parameters:(NSDictionary * _Nullable)params {
__block id result = nil;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
AFHTTPSessionManager *session = [[AFHTTPSessionManager alloc]initWithBaseURL:[NSURL URLWithString:baseURL]];
[session GET:path parameters:params progress:nil success:^(NSURLSessionDataTask *task, id responseObject) {
result = responseObject;
dispatch_semaphore_signal(semaphore);
} failure:^(NSURLSessionDataTask *task, NSError *error) {
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
return result;
}
@end
Si vous appelez le bloc de synchronisation à l'intérieur d'un bloc d'achèvement d'une autre demande AFNetwork, assurez-vous de modifier la propriété completionQueue
. Si vous ne le modifiez pas, le bloc synchrone appellera la file d'attente principale à la fin alors qu'il se trouve déjà dans la file d'attente principale et plantera votre application.
+ (void)someRequest:(void (^)(id response))completion {
AFHTTPSessionManager *session = [[AFHTTPSessionManager alloc]initWithBaseURL:[NSURL URLWithString:@""] sessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
dispatch_queue_t queue = dispatch_queue_create("name", 0);
session.completionQueue = queue;
[session GET:@"path/to/resource" parameters:nil progress:nil success:^(NSURLSessionDataTask *task, id responseObject) {
NSDictionary *data = [session sendSynchronousRequestWithBaseURLAsString:@"" pathToData:@"" parameters:nil ];
dispatch_async(dispatch_get_main_queue(), ^{
completion (myDict);
});
} failure:^(NSURLSessionDataTask *task, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
completion (error);
});
}];