为什么这个循环不会退出

时间:2013-12-21 00:58:33

标签: ios objective-c afnetworking nsoperation

我的假设是operation在一个单独的线程上异步运行,但循环永远不会退出,所以某些东西不是我想象的那样。

/**
 Checks if we can communicate with the APIs

 @result YES if the network is available and all of the registered APIs are responsive
 */
- (BOOL)apisAvailable
{
    // Check network reachability
    if (!_connectionAvailable) {
        return NO;
    }
    // Check API server response
    NSMutableSet *activeOperations = [[NSMutableSet alloc] init];
    __block NSInteger successfulRequests = 0;
    __block NSInteger failedRequests = 0;
    for (AFHTTPClient *httpClient in _httpClients) {
        // Send heart beat request
        NSMutableURLRequest *request = [httpClient requestWithMethod:@"GET" path:@"" parameters:nil];
        AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
        [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
            // Server returned good response
            successfulRequests += 1;
        } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
            // Server returned bad response
            failedRequests += 1;
        }];
        [operation start];
        [activeOperations addObject:operation];
    }
    // Wait for heart beat requests to finish
    while (_httpClients.count > (successfulRequests + failedRequests)) {
        // Wait for each operation to finish, one at a time
        //usleep(150);
        [NSThread sleepForTimeInterval:0.150];
    }
    // Check final results
    if (failedRequests > 0) {
        return NO;
    }
    return YES;
}

2 个答案:

答案 0 :(得分:2)

一些建议:

  • 永远不要检查可达性以确定请求是否成功。你应该试试这个请求;只有当它失败时你才应该咨询可达性,以便尽可能地得到最好的猜测。可达性使 no 保证请求是否会失败或成功。

  • 是否在主线程上调用此方法?即使您使用永不完成的请求修复了问题,它也会在网络请求运行的整个过程中阻止UI。由于这些请求可能需要很长时间,因此对于用户来说这是一种糟糕的体验,如果操作系统在错误的时间(例如在发布时)发生,操作系统将会终止您的应用。

  • 在调用sleep或者等效的时候循环会浪费CPU资源和内存,并且会阻止线程的runloop为任何定时器,事件处理程序或回调提供服务。 (这可能是网络完成块永远无法运行的原因。)如果你可以避免阻塞线程,你应该这样做。另外,如果你在自己没有创造的NSThread上做这件事,Cocoa往往会感到不快。

我看到两个选项:

  1. 使用dispatch_group等待所有请求完成。而不是阻止你的调用线程,你应该在完成时使用完成块来调用。因此,不是返回BOOL,而是选择一个带有BOOL的完成块。像- (void)determineIfAPIIsAvailable:(void(^)(BOOL))completionBlock;

  2. 这样的东西
  3. 完全摆脱这种方法。你用这种方法做什么的?当事情失败时,尝试使用您的API并向用户报告适当的错误几乎肯定是一个更好的主意,而不是试图猜测API的请求是否会事先成功。

答案 1 :(得分:0)

我认为问题在于我没有使用锁定来递增计数器,因此while循环永远不会计算为true

只要它被任何请求回调块递增,然后我就知道该做什么,我只能通过查找大于0的失败计数来使其工作。

我恰好已经切换到[NSOperationQueue waitUntilAllOperationsAreFinished]

最终代码:

/**
 Checks if we can communicate with the APIs

 @result YES if the network is available and all of the registered APIs are responsive
 */
- (BOOL)apisAvailable
{
    // Check network reachability
    if (!_connectionAvailable) {
        return NO;
    }
    // Check API server response
    NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
    __block NSInteger failedRequests = 0;
    for (AFHTTPClient *httpClient in _httpClients) {
        // Send heart beat request
        NSMutableURLRequest *request = [httpClient requestWithMethod:@"GET" path:@"" parameters:nil];
        AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
        [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
            // Server returned good response
        } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
            // Server returned bad response
            failedRequests += 1;
        }];
        [operationQueue addOperation:operation];
    }
    // Wait for heart beat requests to finish
    [operationQueue waitUntilAllOperationsAreFinished];
    // Check final results
    if (failedRequests > 0) {
        return NO;
    }
    return YES;
}