NSURLSessionDataTask和线程

时间:2015-04-30 16:00:04

标签: ios objective-c grand-central-dispatch

我正在使用NSURLSessionDataTask获取JSON提要并填充位于共享存储区内的NSMutableArray,该存储区是一个单例。外部世界可以通过将其强制转换为NSArray的getter访问NSMutableArray。

getter调用一个刷新方法,该方法轮询JSON提要并填充NSMutableArray,如下所示:

- (NSArray *)articles
{

    if ([_articles count] == 0) {

        [self refreshArticles];

    }
    return _articles;
}

以下是一些方法:

NSURLRequest *request = [NSURLRequest requestWithURL:feedURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:4.0];

NSURLSessionDataTask *task = [self.session dataTaskWithRequest:request
                                             completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){

                                                 if (!error && response != nil) {

                                                     // decode JSON and add resultant objects to _articles

                                                     dispatch_async(dispatch_get_main_queue(), ^{
                                                         NSLog(@"Updated feed");
                                                         [nc postNotificationName:@"MLNArticleStoreFeedDidUpdate" object:self];
                                                     });
                                                 } else if (response == nil) {
                                                     NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
                                                     [nc postNotificationName:@"MLNNetworkError" object:self];
                                                 }

                                             }];

[task resume];

这样可行,但每次调用getter时,Feed都会刷新7次。我认为这与getter的if子句在Feed下载时继续为真。我用dispatch_once缓解了这个问题,但它确实有效,但我觉得这样做不对。

以下是代码的内容:

- (NSMutableArray *)articles
{

    if ([_articles count] == 0) {
        static dispatch_once_t onceToken;

        dispatch_once(&onceToken, ^{
            [self refreshArticles];
        });
    }
    return _articles;
}

但我意味着是:"如果没有文章,请去获取一些, 然后 返回" 。有没有更好的方法呢?

1 个答案:

答案 0 :(得分:1)

以这种方式使用的

dispatch_once将不会执行您要执行的操作。这里真实的是你几乎肯定想要在返回之前等待网络活动。如果您阻止这样的主线程,操作系统将终止您的应用程序。

- (NSArray *)articles
{    
    if ([_articles count] == 0) {
        [self refreshArticlesFromNetwork];
    }
    return _articles;
}

- (void)refreshArticlesFromNetwork
{
    if (self.networkRefreshInProgress)
        return;

    self.networkRefreshInProgress = YES;
    [self showNetworkLoadingUI];

    NSURLRequest *request = [NSURLRequest requestWithURL:feedURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:4.0];
    NSURLSessionDataTask *task = [self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){
        NSMutableArray* localArray = [NSMutableArray array];

        if (!error && response != nil) {
            // decode JSON and add resultant objects to local array
            [localArray addObject: ... ];
        }

        dispatch_async(dispatch_get_main_queue(), ^{
            _articles = [localArray copy];
            self.networkRefreshInProgress = NO;
            [self hideNetworkLoadingUI];
            NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
            if (!error && response != nil) {
                [nc postNotificationName:@"MLNArticleStoreFeedDidUpdate" object:self];
            } else if (response == nil) {
                [nc postNotificationName:@"MLNNetworkError" object:self];
            }
            NSLog(@"Updated feed");
        });
    }];

    [task resume];
}

这里的关键要点:

  • 向用户显示一些“正在加载...”UI,而不是阻止主线程。
  • 如果正在进行等效更新,请勿触发其他更新。
  • 不要修改用于从后台线程填充UI的模型状态。