NSURLSessionDataTask 和线程
NSURLSessionDataTask and threading
我正在获取一个带有 NSURLSessionDataTask 的 JSON 提要,并填充一个 NSMutableArray,它位于一个共享存储中,它是一个单例。 NSMutableArray 可以通过 getter 将其转换为 NSArray 供外界访问。
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 时,提要都会刷新 7 次。我认为这与 getter 的 if 子句在 feed 下载时继续为真有关。我已经用 dispatch_once
缓解了这个问题,它起作用了,但我觉得那不对。
现在的代码是这样的:
- (NSMutableArray *)articles
{
if ([_articles count] == 0) {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self refreshArticles];
});
}
return _articles;
}
但我的意思是:"if there are no articles, go get some, and then return"。有更好的方法吗?
dispatch_once
,以这种方式使用,不会做你想做的事。这里的真实情况是,您几乎可以肯定 不想 想要等待网络 activity 后再返回。如果你像那样阻塞主线程,OS 会杀死你的应用程序。
- (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];
}
此处要点:
- 向用户显示一些 "Loading..." UI 而不是阻塞主线程。
- 如果等效更新已经在进行中,则不要触发其他更新。
- 不要修改用于从后台线程填充 UI 的模型状态。
我正在获取一个带有 NSURLSessionDataTask 的 JSON 提要,并填充一个 NSMutableArray,它位于一个共享存储中,它是一个单例。 NSMutableArray 可以通过 getter 将其转换为 NSArray 供外界访问。
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 时,提要都会刷新 7 次。我认为这与 getter 的 if 子句在 feed 下载时继续为真有关。我已经用 dispatch_once
缓解了这个问题,它起作用了,但我觉得那不对。
现在的代码是这样的:
- (NSMutableArray *)articles
{
if ([_articles count] == 0) {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self refreshArticles];
});
}
return _articles;
}
但我的意思是:"if there are no articles, go get some, and then return"。有更好的方法吗?
dispatch_once
,以这种方式使用,不会做你想做的事。这里的真实情况是,您几乎可以肯定 不想 想要等待网络 activity 后再返回。如果你像那样阻塞主线程,OS 会杀死你的应用程序。
- (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];
}
此处要点:
- 向用户显示一些 "Loading..." UI 而不是阻塞主线程。
- 如果等效更新已经在进行中,则不要触发其他更新。
- 不要修改用于从后台线程填充 UI 的模型状态。