使用GCD在NSMutableArray中存储图像

时间:2014-04-17 01:02:31

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

我正在尝试使用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块吗?我需要按顺序命名的数组中的图像。

3 个答案:

答案 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;
    });
});