ios AFNetworking 能否同步返回数据(在块内)?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7969865/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Can AFNetworking return data synchronously (inside a block)?
提问by Shai Mishali
I have a function using AFJSONRequestOperation, and I wish to return the result only after success. Could you point me in the right direction? I'm still a bit clueless with blocks and AFNetworking specifically.
我有一个使用 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.
}
回答by mattt
To block the execution of the main thread until the operation completes, you could do [operation waitUntilFinished]
after it's added to the operation queue. In this case, you wouldn't need the return
in the block; setting the __block
variable would be enough.
要阻止主线程的执行直到操作完成,您可以[operation waitUntilFinished]
在将其添加到操作队列后执行。在这种情况下,您不需要return
块中的 ;设置__block
变量就足够了。
That said, I'd strongly discourage forcing asynchronous operations to synchronous methods. It's tricky to get your head around sometimes, but if there's any way you could structure this to be asynchronous, that would almost certainly be the way to go.
也就是说,我强烈反对将异步操作强制为同步方法。有时很难理解,但如果有任何方法可以将其构建为异步,那几乎肯定是要走的路。
回答by Kasik
I'm using semaphores to solve this issue. This code is implemented in my own class inherited from 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;
回答by Ikhsan Assaat
I would suggest that you don't make a synchronous method with AFNetworking (or blocks in general). A good approach is that you make another method and use the json data from the success block as an argument.
我建议您不要使用 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];
}
回答by joerick
It's worth noting that some features of AFNetworking's AFClient can still be used in a synchronous manner, meaning that you can still use niceties such as Authorisation headers and multipart uploads.
值得注意的是,AFNetworking 的 AFClient 的某些功能仍然可以以同步方式使用,这意味着您仍然可以使用诸如 Authorization 标头和分段上传等细节。
For example:
例如:
NSURLRequest *request = [self.client requestWithMethod: @"GET"
path: @"endpoint"
parameters: @{}];
NSHTTPURLResponse *response = nil;
NSError *error = nil;
NSData *responseData = [NSURLConnection sendSynchronousRequest: request
returningResponse: &response
error: &error];
Remember to check response.statusCode
in this case, as this method doesn't consider HTTP failure codes as errors.
请记住response.statusCode
在这种情况下进行检查,因为此方法不会将 HTTP 失败代码视为错误。
回答by user2976703
Add this below the code you normally work with:
在您通常使用的代码下方添加以下内容:
[operation start];
[operation waitUntilFinished];
// do what you want
// return what you want
Example:
例子:
+ (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];
}
回答by Mark Bourke
To expand/update @Kasik's answer. You can create a category on AFNetworking like so using semaphores:
扩展/更新@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
If you are calling the sync block inside a completion block of another AFNetwork request, make sure you change the completionQueue
property. If you don't change it, the synchronous block will call the main queue upon completion while already on the main queue and will crash your application.
如果您在另一个 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);
});
}];