iphone - GCD 的后进先出堆栈?

标签 iphone objective-c ios uitableview grand-central-dispatch

我有一个 UITableView,它在每一行中显示与联系人关联的图像。在某些情况下,这些图像是在第一次显示时从地址簿联系人图像中读取的,并且在没有这些图像的情况下,它们是基于存储数据呈现的化身。我目前使用 GCD 在后台线程上更新这些图像。但是,这会按照请求的顺序加载图像,这意味着在快速滚动期间队列会变得很长,而当用户停止滚动时,当前单元格是最后得到更新的。在 iPhone 4 上,这个问题并不明显,但我很想支持旧硬件并正在 iPhone 3G 上进行测试。延迟是可以容忍的,但非常明显。

让我印象深刻的是,后进先出堆栈似乎可以在很大程度上解决这个问题,因为每当用户停止滚动时,这些单元格将是下一个要更新的单元格,然后其他当前不在屏幕上的单元格将被更新更新。 Grand Central Dispatch 可以实现这样的事情吗?还是不太繁琐而无法以其他方式实现?

请注意,顺便说一句,我正在将 Core Data 与 SQLite 存储一起使用,并且我没有使用 NSFetchedResultsController ,因为必须遍历多对多关系才能加载数据这个观点。 (据我所知,这排除了使用 NSFetchedResultsController 的可能性。) [我发现 NSFetchedResultsController 可以用于多对多关系,尽管官方文档似乎是这么说的。但我还没有在这种情况下使用它。]

补充:请注意,虽然主题是“如何使用 GCD 创建后进先出堆栈”,但实际上我只是想解决上述问题,可能会有是一种更好的方法。我非常愿意接受 timthetoolman 提出的以另一种方式解决问题的建议;如果这样的建议最终是我使用的,我将认识到原始问题的最佳答案以及我最终实现的最佳解决方案......:)

最佳答案

由于设备的内存限制,您应该按需在后台 GCD 队列中加载图像。在 cellForRowAtIndexPath: 方法中检查您的联系人图像是否为零或已被缓存。如果图像为 nil 或不在缓存中,请使用嵌套的 dispatch_async 从数据库加载图像并更新 tableView 单元格。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
   {
       static NSString *CellIdentifier = @"Cell";
       UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
       if (cell == nil) {
            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
       }
       // If the contact object's image has not been loaded, 
       // Use a place holder image, then use dispatch_async on a background queue to retrieve it.

       if (contact.image!=nil){
           [[cell imageView] setImage: contact.image];
       }else{
           // Set a temporary placeholder
           [[cell imageView] setImage:  placeHolderImage];

           // Retrieve the image from the database on a background queue
           dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
           dispatch_async(queue, ^{
               UIImage *image = // render image;
               contact.image=image;

               // use an index path to get at the cell we want to use because
               // the original may be reused by the OS.
               UITableViewCell *theCell=[tableView cellForRowAtIndexPath:indexPath];

               // check to see if the cell is visible
               if ([tableView visibleCells] containsObject: theCell]){
                  // put the image into the cell's imageView on the main queue
                  dispatch_async(dispatch_get_main_queue(), ^{
                     [[theCell imageView] setImage:contact.image];
                     [theCell setNeedsLayout];
                  });
               }
           }); 
       }
       return cell;
}

WWDC2010 session 视频“Introducing Blocks and Grand Central Dispatch”也展示了使用嵌套 dispatch_async 的示例。

另一个潜在的优化可能是在应用程序启动时开始在低优先级后台队列中下载图像。即

 // in the ApplicationDidFinishLaunchingWithOptions method
 // dispatch in on the main queue to get it working as soon
 // as the main queue comes "online".  A trick mentioned by
 // Apple at WWDC

 dispatch_async(dispatch_get_main_queue(), ^{
        // dispatch to background priority queue as soon as we
        // get onto the main queue so as not to block the main
        // queue and therefore the UI
        dispatch_queue_t lowPriorityQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)
        dispatch_apply(contactsCount,lowPriorityQueue ,^(size_t idx){
               // skip the first 25 because they will be called
               // almost immediately by the tableView
               if (idx>24){
                  UIImage *renderedImage =/// render image
                  [[contactsArray objectAtIndex: idx] setImage: renderedImage];
               }

        });
 });

通过这种嵌套调度,我们在极低优先级队列上渲染图像。将图像渲染放在后台优先级队列中将允许从上面的 cellForRowAtIndexPath 方法渲染的图像以更高的优先级渲染。因此,由于队列优先级的不同,您将有一个“穷人”后进先出法。

祝你好运。

关于iphone - GCD 的后进先出堆栈?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7567827/

相关文章:

iOS PullToRefreshView,添加didFinishRefreshing方法

ios - XmmpFramework + Openfire : manage offline messages

ios - 删除部分时 UITableView 的平滑动画

objective-c - EXC_BAD_ACCESS 我保留、使用然后释放的警报 View 对象

ios - Objective C 为什么我的 NSMutableArray 中的对象消失了?

iOS swift : function not returning correct value

iphone - 为 UIButton 设置一个子类来处理外观

iphone - 水平和垂直两个方向滚动

iphone - 动画完成后调用带参数的方法

iphone - iOS 上的 AES-256 加密不会产生与 openssl 相同的结果