ios - 仅在需要时加载图像

标签 ios image memory load uicollectionview

当我加载要显示给 UICollectionView 的图像时,我会像这样从数组中加载所有图像

- (void)viewDidLoad
{
    allImagesArray = [[NSMutableArray alloc] init];
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *location=@"Others";
    NSString *fPath = [documentsDirectory stringByAppendingPathComponent:location];
    NSArray *directoryContent = [[NSFileManager defaultManager] directoryContentsAtPath: fPath];
    collectionOthers.delegate =self;
    collectionOthers.dataSource=self;
    for(NSString *str in directoryContent)
    {
        NSString *finalFilePath = [fPath stringByAppendingPathComponent:str];
        NSData *data = [NSData dataWithContentsOfFile:finalFilePath];
        if(data)
        {
            UIImage *image = [UIImage imageWithData:data];
            [allImagesArray addObject:image];
            NSLog(@"array:%@",[allImagesArray description]);
            image = nil;

        }
        finalFilePath=nil;
        data=nil;
    }

    paths= nil;
    documentsDirectory= nil;
    location= nil;
    fPath= nil;
    directoryContent = nil;
}

这是我的应用程序中最大的问题,因为它使用了太多内存。这是因为图像的数量和大小,这可能会占用内存。我只想在需要时加载图像,并在不再需要时丢弃它们。但是我不知道在哪里以及如何更改我的代码以使其成为那样。我这样做已经三个月左右了,我真的需要帮助。
更新
这是我的特定部分的代码

-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *reuseID = @"ReuseID";
    OthersCell *mycell = (OthersCell *) [collectionView dequeueReusableCellWithReuseIdentifier:reuseID forIndexPath:indexPath];
    UIImageView *imageInCell = (UIImageView*)[mycell viewWithTag:1];
    imageInCell.image = [allImagesArray objectAtIndex:indexPath.row];
    NSLog(@"a");
    return mycell;
}

最佳答案

显然,您应该及时加载图像。永远不要保存一组图像(因为它们占用大量内存),而应该只保存一组文件名。所以我建议您停用 allImagesArray,而是定义一个名为 filenamesNSMutableArray。然后您可以动态创建 UIImage 对象:

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *cellIdentifier = @"Cell";
    OthersCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
    UIImageView *imageInCell = (UIImageView*)[cell viewWithTag:1];

    imageInCell.image = [UIImage imageWithContentsOfFile:filenames[indexPath.item]];

    return cell;
}

当然,这假定您在 viewDidLoad 中填充了 filenamesNSMutableArray:

- (void)viewDidLoad
{
    filenames = [[NSMutableArray alloc] init];
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *location=@"Others";
    NSString *fPath = [documentsDirectory stringByAppendingPathComponent:location];
    NSArray *directoryContent = [[NSFileManager defaultManager] directoryContentsAtPath: fPath];
    collectionOthers.delegate =self;
    collectionOthers.dataSource=self;
    for(NSString *str in directoryContent)
    {
        NSString *finalFilePath = [fPath stringByAppendingPathComponent:str];
        [filenames addObject:fileFilePath];
    }
}

但是,这有一个问题,因为 imageWithContentsOfFile(以及首先将其加载到 NSData 然后执行 imageWithData)是一个如果图像不是很小,则有点慢。在较慢的设备上,这可能会导致快速滚动 Collection View 时出现轻微的卡顿。因此,更好的方法是 (a) 异步加载图像; (b) 使用 NSCache 优化向后滚动时的性能。

所以,首先,定义一个缓存:

@property (nonatomic, strong) NSCache *imageCache;

然后,在 viewDidLoad 中实例化它:

self.imageCache = [[NSCache alloc] init];
self.imageCache.name = @"com.company.app.imageCache";

然后,cellForItemAtIndexPath 可以 (a) 从缓存中设置图像; (b) 如果未找到,则检索图像并适本地异步更新缓存和单元格,例如:

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *cellIdentifier = @"Cell";
    OthersCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
    UIImageView *imageInCell = (UIImageView*)[cell viewWithTag:1];

    NSString *cacheKey = filenames[indexPath.item];
    imageInCell.image = [self.imageCache objectForKey:cacheKey];

    if (imageInCell.image == nil) {
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            UIImage *image = [UIImage imageWithContentsOfFile:filenames[indexPath.item]];
            if (image) {
                [self.imageCache setObject:image forKey:cacheKey];
                dispatch_async(dispatch_get_main_queue(), ^{
                    OthersCell *updateCell = (id)[collectionView cellForItemAtIndexPath:indexPath];
                    UIImageView *imageInCell = (UIImageView*)[updateCell viewWithTag:1];
                    imageInCell.image = image;
                });
            }
        });
    }

    return cell;
}

而且,显然,如果收到内存警告,请确保清除缓存(在 iOS 7 中,缓存并不总是像过去那样在压力下自动清除自身):

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];

    [self.imageCache removeAllObjects];
}

关于ios - 仅在需要时加载图像,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20032408/

相关文章:

ios - 如何在 tableViewCell 中运行 imagePicker

ios - 是否可以在没有任何互联网连接/禁用蜂窝网络的情况下使用核心定位/GPS?

ios - 从相机获取 yuv 平面格式图像 - iOS

ios - 如何使用PHPhotoKit将CLLocation的GPS信息写入UIImage?

php - 无法显示源图像。 PHP/MYSQL

java - 将自定义 FileFilter 与 JFileChooser 一起使用

python - 在不知道模式的情况下加载非常大的 JSON 文件?

c# - Kentico 7 - 在 web 部件中显示媒体库中的图像

node.js - Node.js 中 RSS 内存增加而堆稳定

java - 总(Tomcat)内存增加但堆没有