在 coredata 中加载图像时 UITableView 滞后

Posted

技术标签:

【中文标题】在 coredata 中加载图像时 UITableView 滞后【英文标题】:UITableView lag when image load in coredata 【发布时间】:2016-02-02 07:37:25 【问题描述】:

我正在使用coredata。我在coredata 中成功添加了图像,但是当我想显示列表时出现问题。当我向下滚动UITableView 时,我遇到了这个滞后问题。我读了这篇文章Loading image from CoreData at cellForRowAtIndexPath slows down scrolling。我仍然有延迟,现在我正在尝试使用LazyLoadImage,但问题是,他们没有使用viewdidload()。我不知道这个LazyLoadImage。我想详细了解 Grand Central Dispatch 以及它的工作原理。

这是我的代码。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

    static NSString *simpleTableIdentifier = @"imageCell";
    
    NSManagedObject *coreData = [self.devices objectAtIndex:indexPath.row];
    
   
    ImageCell *cell = (ImageCell *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
    
    if (cell == nil) 
        cell = [[ImageCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
    
    
   
   dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^
       
       
       UIImage *imagex = [cache objectForKey:[NSNumber numberWithUnsignedInteger:indexPath.row]];
       UIImage * rowImage = [UIImage imageWithData:[coreData valueForKey:@"image"]];
       UIImageView * myImage = [[UIImageView alloc] initWithImage:rowImage];
       myImage.contentMode = UIViewContentModeScaleAspectFill;
       myImage.frame = CGRectMake(0, 0, 79, 56);
       
        cell.name.text = [coreData valueForKey:@"name"];
        if (imagex == nil) 
            
            [cache setObject:myImage.image forKey:[NSNumber numberWithUnsignedInteger:indexPath.row]];
            
          
            imagex = [cache objectForKey:[NSNumber numberWithUnsignedInteger:indexPath.row]];
        
       if(imagex)
       
           dispatch_async(dispatch_get_main_queue(), ^

               if ([[tableView indexPathsForVisibleRows] containsObject:indexPath]) 
                   //check that the relevant data is still required
                   UITableViewCell * correctCell = [self.tableView cellForRowAtIndexPath:indexPath];
                   //get the correct cell (it might have changed)
                   [[correctCell imageView] setImage:imagex];
                   [correctCell setNeedsLayout];
               
           );
       
   );
   

    return cell;
    

如果我将列表放入控制器中,性能是否重要?我这里没有使用 xib。

这里的问题只是第一次加载。当它向下滚动时。有些代码很熟悉。我正在尝试自己解决它,但现在没有运气。我已经在我们的应用程序中阅读了 GCD 的重要性。 请帮助我了解更多相关信息。

更新 这些图像来自我拍摄的相机。我想我需要调整它的大小。

【问题讨论】:

self.devices 是您加载 tableView 的数组吗? @noobsmcgoobs 是的。那是持有 coredata 数据列表的人。 您的数据集有多大?具体来说 - 它是否如此之大以至于您实际上只需要在需要时才检索数据,还是可以将其保存在内存中? 还有一个问题,您使用的是自定义UIImageView 而不是默认contentViewimageView 属性? 对不起,我没有得到数据集的平均值。在哪里可以看到该数据集? 【参考方案1】:

为什么你在 setImage 之后 setNeedsLayout ?我觉得没必要。

【讨论】:

我只是将它复制到其他帖子。我会删除它 还是一样。我在第一次加载时遇到了延迟【参考方案2】:

如果您只有 50 个条目,您可以将它们全部加载 - 在后台队列中 - 并使用图像填充 self.devices。您可以在 viewDidLoad 中或在需要刷新时启动该填充

在 cellForRowAtIndexPath() 中,您需要检查数据是否已填充 - 第一次通过它可能不是表格顶部 - 并显示“等待”图像或消息

在您的数据加载过程中,您应该每 10 条左右的记录调用一次 reloadData(),并在最后一次以确保在数据可用时更新表视图。

使用这种方法,您不会看到加载时间超过表中的前几个条目,并且性能对用户来说看起来不错

【讨论】:

这里的问题是我向下滚动时的第一次加载。我会尝试你的建议 顺便说一句。图像尺寸非常大。图片大小有关系吗? 取决于多大!如果您可以一次加载所有 50 张图像,那么您将在 tableview 中获得更快的移动速度。如果 50 张图片太大以至于加载它们都会导致设备崩溃,那么性能不会那么好:-) 如果它们真的很大,如何在表格中加载预览图像,并在选择时仅加载全尺寸。加载时会有延迟,但除非您计划对表中的每一行执行操作,否则性能影响会降低。 我认为图像的大小是 1905 x1876。该图像来自我拍摄的相机。我会在这里尝试你的建议【参考方案3】:

通过线程创建图像,不能保证图像返回到单元格的速度比UITableView 滚动快。

您的单元格被重复使用,所以我不确定您是否需要此代码

if ([[tableView indexPathsForVisibleRows] containsObject:indexPath]) 
               //check that the relevant data is still required
               UITableViewCell * correctCell = [self.tableView cellForRowAtIndexPath:indexPath];

这可能会导致问题,因为如果单元格部分或部分显示在屏幕上,您将返回一个用户不可见或部分可见的单元格。

您的问题是第一次加载。当您使用setImage 时,操作系统会缓存图像,以便在第一次加载后没有延迟,因此很多缓存代码都无效,因为图像的第一次设置需要初始化对象,然后操作系统缓存它。

您的图像也很大,除非您需要在 iPad 上全屏显示,否则我会缩小尺寸。

【讨论】:

是的,图像非常大。我仍然在这里学习以减少图像。我会更新你。谢谢【参考方案4】:

我建议将图像存储在 Mobile 目录中而不是核心数据中,因为核心数据在其中存储图像效率不高。

您可以将图像存储在示例代码中 1. 将所有图像存储在名为 imageArray 的数组中

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0]; // Get documents folder
    NSString *dataPath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat: @"/%@",self.projectName ]];

    if (![[NSFileManager defaultManager] fileExistsAtPath:dataPath])
        [[NSFileManager defaultManager] createDirectoryAtPath:dataPath withIntermediateDirectories:NO attributes:nil error:&error];


    for (int i=0; i< self.imageArray.count; i++) 
        NSString *savedImagePath = [dataPath stringByAppendingPathComponent:[NSString stringWithFormat:  @"%i.png",i ]];
        UIImage *image = [self.imageArray objectAtIndex:i];
        NSData *imageData = UIImagePNGRepresentation(image);
        if ([[NSFileManager defaultManager] fileExistsAtPath:dataPath])
        
            [[NSFileManager defaultManager] removeItemAtPath:savedImagePath error:&error];

        

     [imageData writeToFile:savedImagePath atomically:NO];
     // 


    

3。然后您可以将图像的地址存储在核心数据中,并使用

从目录中轻松检索
 static NSString *simpleTableIdentifier = @"imageCell";

    NSManagedObject *coreData = [self.devices objectAtIndex:indexPath.row];


    ImageCell *cell = (ImageCell *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];

    if (cell == nil) 
        cell = [[ImageCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
    
UIImage *imagex = [cache objectForKey:[NSNumber numberWithUnsignedInteger:indexPath.row]];

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString *documentsDirectory = [paths objectAtIndex:0]; // Get documents folder
        NSString *dataPath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat: @"/%@",self.projectName ]];

    NSString * imgPath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat: @"/image%@",indexPath.row]];
      NSData *imgData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:imgPath]];

       UIImage * rowImage = [[UIImage alloc] initWithData:imgData];
       UIImageView * myImage = [[UIImageView alloc] initWithImage:rowImage];
       myImage.contentMode = UIViewContentModeScaleAspectFill;
       myImage.frame = CGRectMake(0, 0, 79, 56);

        cell.name.text = [coreData valueForKey:@"name"];
        if (imagex == nil) 

            [cache setObject:myImage.image forKey:[NSNumber numberWithUnsignedInteger:indexPath.row]];


            imagex = [cache objectForKey:[NSNumber numberWithUnsignedInteger:indexPath.row]];
        

【讨论】:

感谢这个人。我会研究这个。这里的另一个知识。我会更新你。再次感谢。【参考方案5】:

如果您在数据库中存储照片、视频或音频(因此,任何可能的大文件),强烈建议使用对它们的引用并使用文件系统来管理内容,特别是如果您计划拥有数百个,可能有数千条记录或更多。

注意:不要忘记异步加载文件。

要记住的事情:当您获取数据时,一切都会占用内存......

..

试试这个解决方法:

打开您的数据模型 选择存储属性“image”的实体 选择存储图片的属性, 然后在右侧菜单“Data Model Inspector”中, 选择: “存储在外部记录文件中”

这样就行了:)

【讨论】:

以上是关于在 coredata 中加载图像时 UITableView 滞后的主要内容,如果未能解决你的问题,请参考以下文章

在 Core Data 中加载具有关系的托管对象时的标准行为是啥?

在后台线程中加载 CoreData

当枢轴选择更改时,如何在枢轴项目中加载图像后加载音频文件

在uiimageview中加载像gif这样的图像时出错

在 UITableView 中加载图像时遇到问题

在响应式网格中加载图像时如何使用图像占位符?