46

我有一个使用 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.
}
4

6 回答 6

56

要阻止主线程的执行直到操作完成,您可以[operation waitUntilFinished]在将其添加到操作队列之后执行。在这种情况下,您不需要returnin 块;设置__block变量就足够了。

也就是说,我强烈反对将异步操作强制用于同步方法。有时很难理解,但如果有任何方法可以将其构建为异步的,那几乎肯定是要走的路。

于 2011-11-01T17:04:55.137 回答
12

我正在使用信号量来解决这个问题。这段代码是在我自己的类中实现的,继承自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;
于 2013-10-02T09:13:29.127 回答
11

我建议您不要使用 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];
}
于 2011-11-01T17:20:23.173 回答
6

值得注意的是,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];

请记住response.statusCode在这种情况下进行检查,因为此方法不会将 HTTP 失败代码视为错误。

于 2013-09-20T17:27:15.527 回答
4

将其添加到您通常使用的代码下方:

[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];
}
于 2013-11-10T17:49:15.543 回答
1

扩展/更新@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);
    });
}];
于 2016-03-15T20:36:09.717 回答