AFNetworking可以同步返回数据(块内)吗?
我有一个使用AFJSONRequestOperation的函数,我只希望成功后返回结果。 你能把我指向正确的方向吗? 我仍然有点无知与块和AFNetworking具体。
-(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. }
要阻止主线程的执行直到操作完成,可以在操作队列中添加[operation waitUntilFinished]
。 在这种情况下,你不需要块中的return
; 设置__block
variables就足够了。
也就是说,我强烈劝阻强制asynchronous操作同步方法。 有时你的头脑很难处理,但是如果有什么办法可以把这个结构变成asynchronous的,那几乎肯定是要走的路。
我正在使用信号来解决这个问题。 这段代码是在我自己的inheritance自AFHTTPClient
的类中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;
我build议你不要与AFNetworking(或一般的块)做一个同步方法。 一个好的方法就是制作另一个方法,并使用来自成功块的json数据作为参数。
- (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]; }
值得注意的是,AFNetworking的AFClient的一些function仍然可以同步使用,这意味着您仍然可以使用授权标头和分段上传等细节。
例如:
NSURLRequest *request = [self.client requestWithMethod: @"GET" path: @"endpoint" parameters: @{}]; NSHTTPURLResponse *response = nil; NSError *error = nil; NSData *responseData = [NSURLConnection sendSynchronousRequest: request returningResponse: &response error: &error];
请记住在这种情况下检查response.statusCode
,因为此方法不考虑HTTP失败代码作为错误。
在通常使用的代码下方添加此代码:
[operation start]; [operation waitUntilFinished]; // do what you want // return what you want
例:
+ (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]; }
扩大/更新@ Kasik的答案。 您可以使用信号量在AFNetworking上创build一个类别:
@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
如果您正在另一个AFNetwork请求的完成块内调用同步块,请确保更改completionQueue
属性。 如果不更改它,同步块将在主队列中完成时调用主队列,并使应用程序崩溃。
+ (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); }); }];