我正在尝试使用GCD块将图像从远程位置存储到NSMutableArray。在viewDidLoad
中调用以下代码,并在UICollectionView中填充图像:
dispatch_apply(self.count, dispatch_get_global_queue(0, 0), ^(size_t i){
NSString *strURL = [NSString stringWithFormat:@"%@%zu%@", @"http://theURL.com/popular/", i, @".jpg"];
NSURL *imageURL = [NSURL URLWithString:strURL];
NSData *imageData = [NSData dataWithContentsOfURL: imageURL];
UIImage *oneImage =[UIImage imageWithData:imageData];
if(oneImage!=nil){
[self.imageArray addObject:oneImage];
}
});
PROBLEM
:图像未被线性存储。
例如。
[self.imageArray objectAtIndex:2]
不是2.jpg
即使它设置了第一张和最后一张图像,其余部分都是混乱的。
另一种方法(我基本上需要的,减去消耗的时间和内存开销):
for (int i=0; i<=[TMAPopularImageManager sharedInstance].numberOfImages-1; i++){
NSString *strURL = [NSString stringWithFormat:@"%@%d%@", @"http://theURL.com/popular/", i, @".jpg"];
NSURL *imageURL = [NSURL URLWithString:strURL];
NSData *imageData = [NSData dataWithContentsOfURL: imageURL];
UIImage *oneImage =[UIImage imageWithData:imageData];
if(oneImage!=nil){
[self.imageArray addObject:oneImage];
}
}
在这种情况下,有更好的方法来实现GCD块吗?我需要按顺序命名的数组中的图像。
答案 0 :(得分:1)
不确定是否适用于您的情况,但如果图像将在UICollectionView中显示,则最好在需要时加载它们。也就是说,当调用委托方法(cellFor ...)时。 这样,您只需在需要时加载所需内容。
如果您需要将数组中的图像用于其他目的,请忽略此答案。
答案 1 :(得分:1)
两种方式:事后对数组进行排序
或
// assume self.imageArray is empty
for (int i = 0; i < self.count; ++i) // fill array with NSNull object
[self.imageArray addObject:[NSNull null]]];
dispatch_apply(self.count, dispatch_get_global_queue(0, 0), ^(size_t i){
NSString *strURL = [NSString stringWithFormat:@"%@%zu%@", @"http://theURL.com/popular/", i, @".jpg"];
NSURL *imageURL = [NSURL URLWithString:strURL];
NSData *imageData = [NSData dataWithContentsOfURL: imageURL];
UIImage *oneImage =[UIImage imageWithData:imageData];
if(oneImage!=nil){
self.imageArray[i] = oneImage;
}
});
我不确定以这种方式操作NSMutableArray
是否是线程安全的,您可能需要
@synchronized(self.imageArray) {
self.imageArray[i] = oneImage;
}
答案 2 :(得分:1)
我跑了大约10次,并且没有按顺序打印一次:
NSInteger iterations = 10;
dispatch_apply(iterations, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, (unsigned long) NULL), ^(size_t index) {
NSLog(@"%zu", index);
});
我的建议,以及我过去所做的,就是在后台线程的块中运行for循环:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, (unsigned long) NULL);
dispatch_async(queue, ^{
int iterations = 0;
for (int x = 0; x < iterations; ++x) {
// Do stuff here
}
dispatch_async(dispatch_get_main_queue(), ^{
// Set content generated on background thread to main thread
});
});
使用后台线程时,务必确保在主线程上初始化的对象是线程安全的,或者在后台初始化对象,然后使用后台线程设置主线程的对象。像上例中那样创建了对象。核心数据尤其如此。
修改强>
似乎使用dispatch_apply的迭代立即返回,因此在执行任何有意义的操作时它们可能会无序执行。如果您运行这两项,您会看到printf
始终按顺序运行,但NSLog
不会:
NSInteger iterations = 10;
dispatch_apply(iterations, the_queue, ^(size_t idx) {
printf("%zu\n", idx);
});
dispatch_apply(iterations, the_queue, ^(size_t idx) {
NSLog(@"%zu\n", idx);
});
在我看来,如果订单很重要,最好在后台线程中运行语句而不是dispatch_apply。
编辑2:
这将是您的实施:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, (unsigned long) NULL);
dispatch_async(queue, ^{
int iterations = 0;
NSMutableArray *backgroundThreadImages = [NSMutableArray array];
for (int i = 0; i < iterations; ++i) {
NSString *strURL = [NSString stringWithFormat:@"%@%i%@", @"http://theURL.com/popular/", i, @".jpg"];
NSURL *imageURL = [NSURL URLWithString:strURL];
NSData *imageData = [NSData dataWithContentsOfURL: imageURL];
UIImage *oneImage =[UIImage imageWithData:imageData];
if(oneImage!=nil){
[backgroundThreadImages addObject:oneImage];
}
}
dispatch_async(dispatch_get_main_queue(), ^{
self.imageArray = backgroundThreadImages;
});
});