2010-02-13 63 views
10

我正在创建一个iPhone应用程序,它将从Web API(包括电子邮件地址)中提取数据。我想在表格单元格中显示与每个电子邮件地址相关联的图像,因此我正在搜索地址簿中的图像,并且如果电子邮件地址不在书中,则会回到默认值。这个伟大的工程,但我有几个担心:什么是在iPhone上进行异步图像缓存的最佳方法?

  • 性能:我已经找到了要找的电子邮件地址(或电话号码)地址簿记录食谱据报道rather slow。原因在于必须迭代每个地址簿记录,并且为每个具有图像的人重复遍历所有电子邮件地址以找到匹配项。当然,这对于大型地址簿来说可能非常耗时。

  • 表格单元:所以我想我会收集所有的电子邮件地址,我需要找到图像并一次找到它们。通过这种方式,我只对所有地址遍历该书一次。但是这对于表格单元格不适用,其中每个单元格对应一个电子邮件地址。我要么必须在显示任何单元格之前收集所有图像(可能会很慢),要么每个单元格在加载时都会查看每个图像(甚至更慢,因为我需要遍历书籍以找到匹配每个电子邮件地址)。

  • 异步查找:那么我想我会查找他们批量,但异步使用NSInvocationOperation。对于AddressBook中的每个图像,我都会在应用程序沙箱中保存一个缩略图。然后每个单元格都可以引用该文件,并显示缺省值(如果它不存在)(因为它不在书中或尚未找到)。如果稍后在异步查找中找到图像,下次需要显示图像时,图像会突然出现。这可能适用于图像的定期再生(例如,在地址簿中更改图像时)。但是,对于我的应用程序的任何给定实例,图像可能实际上不会显示一段时间。

  • 异步表格单元格查找:理想情况下,我会使用类似markjnet's asynchronous table cell updating更新表格单元格中的图像一旦被下载。但是为了达到这个目的,我们不得不为每个单元显示NSInvocationOperation作业,因为它显示的内容以及沙盒中缺少缓存图标。但之后我们又回到了低效地遍历整个地址簿 - 如果您刚刚下载了一大堆新的电子邮件地址,那么这可能会是很多问题。

所以我的问题是:别人怎么做到这一点?我在摆弄Tweetie2,看起来它会异步更新显示的表格单元格。我假设它发送一个单独的HTTP请求为它需要的每个图像。如果是这样,我想通过电子邮件地址搜索本地地址簿效率不是那么低,所以也许这是最好的方法?只是不担心与搜索地址簿相关的性能问题?

如果是这样,在沙箱中保存缩略图图像是缓存的最佳方法?如果我想创建一份新工作来更新地址簿中所有更改的缩略图,每天一次,那么最好的方法是什么?

你们其他人如何解决这类问题?建议将非常感谢!

回答

2

,我只会每次你得到一个时间做一次通过地址簿数据如果可能的话,一批电子邮件地址。 (是的,我会这样做异步。)

创建一个NSMutableDictionary将作为您的内存缓存的搜索结果。使用下载中的每个电子邮件地址作为密钥初始化此字典,并将标记作为该密钥的值(例如[NSNull null])。

接下来,遍历地址簿中的每个ABRecordRef,调用ABRecordCopyValue(record, kABPersonEmailProperty)并遍历返回的每个ABMultiValue中的结果。如果任何电子邮件地址是缓存中的密钥,请将[NSNumber numberWithInt:ABRecordGetRecordId(record)]设置为字典中该密钥的值。

使用此字典作为查找索引,您可以快速获取ABRecordRefs的图像,仅针对当前在表视图中显示的电子邮件地址,如用户当前的滚动位置所示,如hoopjones的回答中所建议的。如果您的应用程序需要该级别的“最新版本”,您可以添加通讯簿更改侦听器以使缓存失效,触发另一个索引操作,然后更新视图。

+0

谢谢。实际上,我已经实现了一种可以一次查找一个地址的解决方案,但它会创建一个缩略图并将其保存到沙盒Documents目录中。是的,它只在第一次需要显示地址簿时查找地址簿中的图像。我*认为从沙盒加载缓存的图像应该足够快,不需要是异步的。 我不需要您描述的最新级别,所以接下来我将介绍如何计划缓存缩略图的定期更新 - 是的,我只会遍历地址簿一次为所有人。 – theory 2010-02-16 00:08:57

+0

只是好奇,你怎么能一次查找一个地址,但只能通过地址簿迭代一次? – erikprice 2010-02-17 16:37:58

2

我会使用您列出的最后一种方法(异步表单元格查找),但只查看正在显示的当前记录的图像。我重载了UIScrollViewDelegate方法来找出用户何时停止滚动,然后才开始请求当前可见单元格。

像这样的东西(这是稍微从教程中,我发现在网络上修改的,我现在找不到,道歉不引用的作者):

- (void)loadContentForVisibleCells 
{ 
    NSArray *cells = [self.table visibleCells]; 
    [cells retain]; 
    for (int i = 0; i < [cells count]; i++) 
    { 
     // Go through each cell in the array and call its loadContent method if it responds to it. 
     AddressRecordTableCell *addressTableCell = (AddressRecordTableCell *)[[cells objectAtIndex: i] retain]; 
     [addressTableCell loadImage]; 
     [addressTableCell release]; 
     addressTableCell = nil; 
    } 
    [cells release]; 
} 


- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView; 
{ 
    // Method is called when the decelerating comes to a stop. 
    // Pass visible cells to the cell loading function. If possible change 
    // scrollView to a pointer to your table cell to avoid compiler warnings 
    [self loadContentForVisibleCells]; 
} 


- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate; 
{ 
    if (!decelerate) 
    { 
     [self loadContentForVisibleCells]; 
    } 
} 

一旦你知道什么是地址记录目前可见,只是搜索那些(可能是5 -7条记录)将会闪电般快速。一旦您抓取图像,只需将其缓存在字典中,以便以后不必重新处理图像请求。

+0

只是出于好奇,为什么这个示例代码保留的NSArray的单元格,然后每个AddressRecordTableCell在对它们进行处理之前,然后在'loadContentForVisibleCells'中释放它们?我没有看到在执行该方法期间可能导致它们被释放的原因。 – erikprice 2010-02-15 14:44:35

+2

你是对的。它不需要保留。当我刚刚学习可可时,我写了这个代码的方式,所以它里面有一些noob-ness :) – hoopjones 2010-02-15 19:33:01

+0

['UIScrollViewDelegate'](http://developer.apple.com/iphone/library/documentation/的UIKit /参考/ UIScrollViewDelegate_Protocol /参考/ UIScrollViewDelegate.html)。这也是由以下yonel链接的[LazyTableImages示例](http://developer.apple.com/iphone/library/samplecode/LazyTableImages/index.html)所使用的。 有趣的是,Tweetie 2似乎没有使用这种方法。当我快速滚动时,滚动时可以看到加载的图像。它总是让我恼火,苹果应用程序不会在滚动时加载图像,而Tweetie 2似乎表明它不是必需的。 – theory 2010-02-16 00:05:30

1

您似乎试图在UITableView中加载惰性图像加载。 有来自苹果一个很好的例子,我在这里引用它:无论你用什么策略图像的实际缓存 Lazy load images in UITableView

+0

非常感谢链接到'LazyTableImages'示例。我没有意识到这一点,并很高兴有它学习。 – theory 2010-02-16 00:06:07

相关问题