Objective c 在继续之前等待AFNetworking完成块(即同步)

Objective c 在继续之前等待AFNetworking完成块(即同步),objective-c,afnetworking-2,Objective C,Afnetworking 2,我有一个AFNetworking同步运行的用例(详情如下)。我怎样才能做到这一点 这是我的代码片段,我已经尽可能地简化了它 我想返回成功响应,但我只得到nil(因为函数在调用块之前返回) 详细信息 所以我需要它同步运行的原因是因为我正在构建一个pod,它将在启动时引导应用程序。引导绑定命中一个服务并在本地保存一组值。然后,这些值将用于当前会话,并且不会更改。如果值发生变化,用户将获得一种奇怪的体验,因此我必须避免这种情况 如果服务中断,没关系。我们将使用默认值或查找上一个会话中保存的一些值,但无

我有一个AFNetworking同步运行的用例(详情如下)。我怎样才能做到这一点

这是我的代码片段,我已经尽可能地简化了它

我想返回成功响应,但我只得到nil(因为函数在调用块之前返回)

详细信息 所以我需要它同步运行的原因是因为我正在构建一个pod,它将在启动时引导应用程序。引导绑定命中一个服务并在本地保存一组值。然后,这些值将用于当前会话,并且不会更改。如果值发生变化,用户将获得一种奇怪的体验,因此我必须避免这种情况

如果服务中断,没关系。我们将使用默认值或查找上一个会话中保存的一些值,但无论发生什么情况,我们都不希望用户的体验在会话中发生更改


(这是一个用于A/B测试和实验的引擎——如果这有助于您“获取”用例的话)。

听起来您的应用程序有一部分在启动请求完成之前无法运行。但是还有一个可以运行的部分(比如启动请求的部分)。给那个部分一个UI,告诉用户我们正忙着准备。没有阻塞UI

但是如果你必须这样做,如果AFNetworking没有提供请求的阻塞版本(这是他们的荣幸),那么你总是可以用老式的方式阻塞

- (void)pleaseDontUseThisIdea {

    __block BOOL thePopeIsCatholic = YES;
    [manager GET: ....^{
        // ...
        thePopeIsCatholic = NO;
    }];

    while (thePopeIsCatholic) {}
}

听起来你的应用程序有一部分在启动请求完成之前无法运行。但是还有一个可以运行的部分(比如启动请求的部分)。给那个部分一个UI,告诉用户我们正忙着准备。没有阻塞UI

但是如果你必须这样做,如果AFNetworking没有提供请求的阻塞版本(这是他们的荣幸),那么你总是可以用老式的方式阻塞

- (void)pleaseDontUseThisIdea {

    __block BOOL thePopeIsCatholic = YES;
    [manager GET: ....^{
        // ...
        thePopeIsCatholic = NO;
    }];

    while (thePopeIsCatholic) {}
}

忽略我们通常不希望发出同步网络请求的事实,传统的解决方案是使用信号量:

- (id)sendForUrl:(NSURL *)url {
    AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];

    manager.completionQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

    __block id response;

    [manager GET:url.absoluteString parameters:nil success: ^(AFHTTPRequestOperation *operation, id responseObject) {
        response = responseObject;
        NSLog(@"JSON: %@", responseObject);
        dispatch_semaphore_signal(semaphore);
    } failure: ^(AFHTTPRequestOperation *operation, NSError *error) {
        NSLog(@"Error: %@", error);
        dispatch_semaphore_signal(semaphore);
    }];

    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

    return response;
}
这里有两个问题:

  • 我知道你说过你不想“看到一些复杂的分派样式的东西”,但是,与其他的东西相比,一个信号量要比一个旋转的循环要好,循环要一直轮询,直到一些值发生变化

  • 注意,假设您是从主线程调用此方法,则必须将
    completionQueue
    设置为其他后台队列。默认情况下,
    AFHTTPRequestOperationManager
    将使用这些完成块的主队列。如果您阻止该线程,直到完成阻止在同一线程上运行,您将死锁(即,您的应用程序将冻结)


  • 为了完整性起见,我要指出,当有人问“如何使某个异步方法同步运行”时,正确的答案总是“你没有”。相反,您通常采用异步模式,例如更改方法以获取块参数:

    - (void)sendForUrl:(NSURL *)url completionHandler:(void (^)(id responseObject, NSError *error))completionHandler {
        AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
    
        manager.completionQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        [manager GET:url.absoluteString parameters:nil success: ^(AFHTTPRequestOperation *operation, id responseObject) {
            if (completionHandler) {
                completionHandler(responseObject, nil);
            }
        } failure: ^(AFHTTPRequestOperation *operation, NSError *error) {
            if (completionHandler) {
                completionHandler(nil, error);
            }
        }];
    }
    
    然后,您可以这样调用它,提供一个完成处理程序块:

    [object sendForURL:url completionHandler:^(id responseObject, NSError *error) {
        // use responseObject and/or error here
    }];
    
    // don't use them here
    

    忽略我们通常不希望发出同步网络请求的事实,传统的解决方案是使用信号量:

    - (id)sendForUrl:(NSURL *)url {
        AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
    
        manager.completionQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    
        __block id response;
    
        [manager GET:url.absoluteString parameters:nil success: ^(AFHTTPRequestOperation *operation, id responseObject) {
            response = responseObject;
            NSLog(@"JSON: %@", responseObject);
            dispatch_semaphore_signal(semaphore);
        } failure: ^(AFHTTPRequestOperation *operation, NSError *error) {
            NSLog(@"Error: %@", error);
            dispatch_semaphore_signal(semaphore);
        }];
    
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    
        return response;
    }
    
    这里有两个问题:

  • 我知道你说过你不想“看到一些复杂的分派样式的东西”,但是,与其他的东西相比,一个信号量要比一个旋转的循环要好,循环要一直轮询,直到一些值发生变化

  • 注意,假设您是从主线程调用此方法,则必须将
    completionQueue
    设置为其他后台队列。默认情况下,
    AFHTTPRequestOperationManager
    将使用这些完成块的主队列。如果您阻止该线程,直到完成阻止在同一线程上运行,您将死锁(即,您的应用程序将冻结)


  • 为了完整性起见,我要指出,当有人问“如何使某个异步方法同步运行”时,正确的答案总是“你没有”。相反,您通常采用异步模式,例如更改方法以获取块参数:

    - (void)sendForUrl:(NSURL *)url completionHandler:(void (^)(id responseObject, NSError *error))completionHandler {
        AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
    
        manager.completionQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        [manager GET:url.absoluteString parameters:nil success: ^(AFHTTPRequestOperation *operation, id responseObject) {
            if (completionHandler) {
                completionHandler(responseObject, nil);
            }
        } failure: ^(AFHTTPRequestOperation *operation, NSError *error) {
            if (completionHandler) {
                completionHandler(nil, error);
            }
        }];
    }
    
    然后,您可以这样调用它,提供一个完成处理程序块:

    [object sendForURL:url completionHandler:^(id responseObject, NSError *error) {
        // use responseObject and/or error here
    }];
    
    // don't use them here
    

    真棒的回答。我担心我会看到一些复杂的调度风格的东西,但这两个建议都很好,完全有意义。谢谢真棒的回答。我担心我会看到一些复杂的调度风格的东西,但这两个建议都很好,完全有意义。谢谢除非信号量是静态的,否则
    dispatch\u release
    it不是一个好主意吗?如果使用ARC,这将为您解决。如果不使用ARC,那么,是的,您将
    分派\u release
    它。每天学习新的东西!很不错的。谢谢你的回答。关于派遣的事情,我的意思是我真的不想让霍恩AFN按照我需要的方式行事。我想我会做的是@danh的第一个建议,那就是在我的API中公开一个BOOL,类似于“bootstrappingIsDone”,这样使用我的Pod的开发人员就可以处理他们认为合适的等待。尽管如此,在我这么做之前,我想我会快速浏览一下NS*API,看看是否可以使用它。(我对ObjC非常陌生,所以仍在研究如何将所有这些结合起来)。谢谢除非信号量是静态的,否则
    dispatch\u release
    it不是一个好主意吗?如果使用ARC,这将为您解决。如果不使用ARC,那么,是的,您将
    分派\u release
    它。每天学习新的东西!很不错的。谢谢你的回答。关于派遣的事情,我的意思是,我真的不想让霍恩·阿夫恩按照我需要的方式行事