Xcode 带有 NSURLSessionDataTask 的 URL 循环数组
Xcode loop array of URLs with NSURLSessionDataTask
我一定是把它做得比现在更难了……或者实施我在网上看到的解决方案不正确。
我有一个 URL 数组,我想遍历这些 URL 并将结果按顺序或数组推送到字典中。我怎样才能让它在 运行 下一个请求之前等待字典更新?基本上我想在后台线程中同步调用。
这里是我调用下载的地方:
for (NSString *path in paths) {
NSURLSession *session = [NSURLSession sessionWithConfiguration [NSURLSessionConfiguration defaultSessionConfiguration]];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:path]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:10];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error)
{
}
else
{
NSError *parsingError = nil;
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data
options:NSJSONReadingAllowFragments
error:&error];
if (parsingError)
{
}
else
{
[myDictionary addObject:dict];
}
}
}];
[task resume];
}
除非一个请求在发出之前确实需要前一个请求的结果(这里不是这种情况),否则您应该 而不是 运行 它们的顺序。按顺序发出可能感觉更合乎逻辑,但这样做会付出巨大的性能损失。同时发出它们,将结果保存在一些无序结构(如字典)中,然后在完成所有操作后,构建您的有序结构。
NSMutableDictionary *results = [NSMutableDictionary dictionaryWithCapacity:[paths count]];
// don't create new session for each request ... initialize this outside of the loop
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]]; // or since you're not doing anything special here, use `sharedSession`
// since we're going to block a thread in the process of controlling the degree of
// concurrency, let's do this on a background queue; we're still blocking
// a GCD worker thread as these run (which isn't ideal), but we're only using
// one worker thread.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// only do for requests at a time, so create queue a semaphore with set count
dispatch_semaphore_t semaphore = dispatch_semaphore_create(4); // only do four requests at a time
// let's keep track of when they're all done
dispatch_group_t group = dispatch_group_create();
// now let's loop through issuing the requests
for (NSString *path in paths) {
dispatch_group_enter(group); // tell the group that we're starting another request
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); // wait for one of the four slots to open up
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:path]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:10];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error) {
// ...
} else {
NSError *parsingError = nil;
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
if (parsingError) {
} else {
// synchronize updating of dictionary
dispatch_async(dispatch_get_main_queue(), ^{
results[path] = dict;
});
}
}
dispatch_semaphore_signal(semaphore); // when done, flag task as complete so one of the waiting ones can start
dispatch_group_leave(group); // tell the group that we're done
}];
[task resume];
}
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// trigger whatever you want when they're all done
// and if you want them in order, iterate through the paths and pull out the appropriate result in order
for (NSString *path in paths) {
// do something with `results[path]`
}
});
});
我试图在这里减少额外依赖的数量,所以我使用了调度组和信号量。上面我用信号量来约束并发度,用dispatch group来判断什么时候都完成了。
就个人而言,我不会使用信号量和组,而是将这些请求包装在异步 NSOperation
子类中,但我试图限制对您的代码所做的更改。但是NSOperation
的思路在逻辑上和上面是一样的:运行他们并发,但是限制了并发的程度,这样你就不会以他们超时而结束,并触发检索仅当检索到所有结果时才显示结果。
我一定是把它做得比现在更难了……或者实施我在网上看到的解决方案不正确。
我有一个 URL 数组,我想遍历这些 URL 并将结果按顺序或数组推送到字典中。我怎样才能让它在 运行 下一个请求之前等待字典更新?基本上我想在后台线程中同步调用。
这里是我调用下载的地方:
for (NSString *path in paths) {
NSURLSession *session = [NSURLSession sessionWithConfiguration [NSURLSessionConfiguration defaultSessionConfiguration]];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:path]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:10];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error)
{
}
else
{
NSError *parsingError = nil;
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data
options:NSJSONReadingAllowFragments
error:&error];
if (parsingError)
{
}
else
{
[myDictionary addObject:dict];
}
}
}];
[task resume];
}
除非一个请求在发出之前确实需要前一个请求的结果(这里不是这种情况),否则您应该 而不是 运行 它们的顺序。按顺序发出可能感觉更合乎逻辑,但这样做会付出巨大的性能损失。同时发出它们,将结果保存在一些无序结构(如字典)中,然后在完成所有操作后,构建您的有序结构。
NSMutableDictionary *results = [NSMutableDictionary dictionaryWithCapacity:[paths count]];
// don't create new session for each request ... initialize this outside of the loop
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]]; // or since you're not doing anything special here, use `sharedSession`
// since we're going to block a thread in the process of controlling the degree of
// concurrency, let's do this on a background queue; we're still blocking
// a GCD worker thread as these run (which isn't ideal), but we're only using
// one worker thread.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// only do for requests at a time, so create queue a semaphore with set count
dispatch_semaphore_t semaphore = dispatch_semaphore_create(4); // only do four requests at a time
// let's keep track of when they're all done
dispatch_group_t group = dispatch_group_create();
// now let's loop through issuing the requests
for (NSString *path in paths) {
dispatch_group_enter(group); // tell the group that we're starting another request
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); // wait for one of the four slots to open up
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:path]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:10];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error) {
// ...
} else {
NSError *parsingError = nil;
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
if (parsingError) {
} else {
// synchronize updating of dictionary
dispatch_async(dispatch_get_main_queue(), ^{
results[path] = dict;
});
}
}
dispatch_semaphore_signal(semaphore); // when done, flag task as complete so one of the waiting ones can start
dispatch_group_leave(group); // tell the group that we're done
}];
[task resume];
}
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// trigger whatever you want when they're all done
// and if you want them in order, iterate through the paths and pull out the appropriate result in order
for (NSString *path in paths) {
// do something with `results[path]`
}
});
});
我试图在这里减少额外依赖的数量,所以我使用了调度组和信号量。上面我用信号量来约束并发度,用dispatch group来判断什么时候都完成了。
就个人而言,我不会使用信号量和组,而是将这些请求包装在异步 NSOperation
子类中,但我试图限制对您的代码所做的更改。但是NSOperation
的思路在逻辑上和上面是一样的:运行他们并发,但是限制了并发的程度,这样你就不会以他们超时而结束,并触发检索仅当检索到所有结果时才显示结果。