Ios AFNetworking能否同步返回数据(在块内)?

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

我有一个使用AFJSONRequestOperation的函数,我希望只在成功后返回结果。你能给我指一下正确的方向吗?我对积木和AFM还是有点不懂

-(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];
对块没有影响。