Ios AFNetworking能否同步返回数据(在块内)?
我有一个使用AFJSONRequestOperation的函数,我希望只在成功后返回结果。你能给我指一下正确的方向吗?我对积木和AFM还是有点不懂Ios AFNetworking能否同步返回数据(在块内)?,ios,objective-c-blocks,synchronous,afnetworking,Ios,Objective C Blocks,Synchronous,Afnetworking,我有一个使用AFJSONRequestOperation的函数,我希望只在成功后返回结果。你能给我指一下正确的方向吗?我对积木和AFM还是有点不懂 -(id)someFunction{ __block id data; AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest
-(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]
。在这种情况下,您不需要块中的返回
;设置\u块
变量就足够了
也就是说,我强烈反对将异步操作强制到同步方法。有时候,你的头脑是很难理解的,但是如果有任何方法可以将其构造为异步的,那几乎肯定是一种方法。我建议你不要使用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的某些功能仍然可以以同步方式使用,这意味着您仍然可以使用诸如授权标头和多部分上载之类的细节 例如:
NSURLRequest *request = [self.client requestWithMethod: @"GET"
path: @"endpoint"
parameters: @{}];
NSHTTPURLResponse *response = nil;
NSError *error = nil;
NSData *responseData = [NSURLConnection sendSynchronousRequest: request
returningResponse: &response
error: &error];
在这种情况下,请记住检查<代码>响应。StaseCu码< /代码>,因为该方法不考虑HTTP失败代码作为错误。 < P>我使用信号量来解决这个问题。这段代码在继承自
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;
将此添加到您通常使用的代码下面:
[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上创建一个类别,如下所示:
@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);
});
}];
嘿,马特,谢谢你的回复。通常我是异步使用数据的,但具体来说,我必须从API返回一些数据,所以我真的看不到其他方法,除非您可以推荐某种方法来执行此操作:)您可以始终向方法添加块参数,如
-someFunctionWithBlock:^(NSData*data){…}
。不幸的是,waitUntilFinished技巧对我不起作用。我有两种本质上是同步的业务方法。遗憾的是,AFNetworking完全忽略了这样的用例。我怀疑waitUntilFinished
技巧对某些人不起作用,因为成功和失败块(默认情况下)是在操作完成后在主队列上使用dispatch\u async
执行的。如果您不是在运行循环(例如单元测试)中执行,那么程序可能会提前退出,而不给GCD运行回调的机会;它不应该强制一个特定的模型,尽管它可能会建议一个。是否需要将json
保留在某个地方,这样实例就不会被释放?我假设AFNetworking代码正在自动删除它。在ARC下,当块执行时,它将被块保留。或者更现代的方式,使用NSNotification
。无用!你知道吗,如果你的应用程序以具体的顺序发送请求,那么在大多数情况下,这并不意味着应用程序将以相同的顺序处理响应?我发现的唯一方法是同步请求和promisekit(以及类似的库,其中self.client代表AfHttpClients的实例)。这非常适合我的需要,谢谢。我想要一个方法,可以从客户端上运行的调试器调用,该方法将提供“curl”与针对REST后端的查询一样,无需重新实现客户端已经管理的OAUTH yack Shaning。它可能也适用于测试和其他非交互式任务。[operation waitUntilFinished];
对块没有影响。