UITables With Downloaded Images - Easy Asynchronous Code <UITable 异步加载图片>
2011-06-20 13:23
471 查看
本文翻译来自:http://www.markj.net/iphone-asynchronous-table-image/
在开发iPho ne&iPad应用时,利用UITable从指定的URL中加载图片是经常遇到的。如果采用单线程的同步加载显然不能满足用户的体验要求,所以我们需要利用多线程的方法在应用程序后台并行的去加载图片。但是多线程编程是比较难的,你需要考虑很多线程安全方面的问题。那怎样才能在避免多线程的情况下异步加载图片了?Cocoa提供了一个精彩的设计思维:
怎么样才能鱼和熊掌兼得了?每一个iPhone应用程序都是一个多线程程序,或者至少是会同多线程iPhone操作系统一起运行。在合适的地方利用恰当的代理方法,你可以有效的利用iPhone已经为你免费提供的多线程实现,你自己不需要编写任何的多线程代码,这样就可以避免一些多线程方面的bug。iPhone应用程序是一个很大的事件循环(如图,可参考Cocoa programming for max os X)——当事件发生时将触发你编写的类的方法。当你使用URL加载系统的异步API时,iPhone将使用不同的线程而不是当前运行应用程序的事件循环(event loop)线程来加载URL对应的内容。当数据加载完成后,将通过event loop产生回调。
根据以上描述,在开发中就可以编写如下简单代码:
需要注意的是,当数据从远程服务器返回时,其他正在进行下载的iPhone线程不能在你的方法正在调用的同时调用你的对象,它是把消息放入你的应用event loop中。假如它直接调用了你的应用程序,可能恰好你的应用程序正在运行UI代码或者其他一些事情,那么你就不得不去编写线程安全的代码。所以数据到达的调用是作为一个事件存在于event loop中。event loop中的事件是运行在一个单线程中,每次执行一个事件。利用这些,我们可以从Flickr那异步的加载图片而不需要我们自己编写线程安全的代码。更好的是,Cocoa的URL加载系统将并行的加载这些URL。
但是,我们怎么在加载完图片后更新UITableViewCell了?UIImage是不可变,也就是说,当你加载完一个图片后就不能改变图片的数据了。值得庆幸的是苹果使得这项工作变得很简单。将UIView对象放入UITableViewCell中,而不是直接放入UIImage。开始时,你的View Object可以为空,或者它可以有一个虚假的图片在里面,或者你可以展示一些“something is happening”视图。当图片数据已经加载完全,创建一个UIImageView,设置图片,然后将它放入table cell中。
作者把这些功能封装成了一个类(AsyncImageView),具体代码如下。使用很方便,只需如下几个步骤:
1、alloc and initWithRect
2、add it to a view, eg in a table cell's content view;
3、send it the loadImageFromURL: message.
利用上面封装的一个类,可以编写一个UITableViewCell使用例子。代码如下。The AsyncImageView gets tagged with 999, and when it gets recycled, that 999 tagged view gets fished out and removed. So only the cell is being recycled, not the AsyncImageView object. When its removed from the cells content view it also gets released, causing dealloc, which in turn cancels the url download (if its outstanding).
在开发iPho ne&iPad应用时,利用UITable从指定的URL中加载图片是经常遇到的。如果采用单线程的同步加载显然不能满足用户的体验要求,所以我们需要利用多线程的方法在应用程序后台并行的去加载图片。但是多线程编程是比较难的,你需要考虑很多线程安全方面的问题。那怎样才能在避免多线程的情况下异步加载图片了?Cocoa提供了一个精彩的设计思维:
UIView heirachy + URL loading system + delegate design = multi-threaded image loading with no multi-threaded coding!
怎么样才能鱼和熊掌兼得了?每一个iPhone应用程序都是一个多线程程序,或者至少是会同多线程iPhone操作系统一起运行。在合适的地方利用恰当的代理方法,你可以有效的利用iPhone已经为你免费提供的多线程实现,你自己不需要编写任何的多线程代码,这样就可以避免一些多线程方面的bug。iPhone应用程序是一个很大的事件循环(如图,可参考Cocoa programming for max os X)——当事件发生时将触发你编写的类的方法。当你使用URL加载系统的异步API时,iPhone将使用不同的线程而不是当前运行应用程序的事件循环(event loop)线程来加载URL对应的内容。当数据加载完成后,将通过event loop产生回调。
根据以上描述,在开发中就可以编写如下简单代码:
connection =[ [NSURLConnection alloc] initWithRequest: request delegate:self]; -(void) connection:(NSURLConnection *)theConnection didReceiveData:(NSData *)incrementalData
需要注意的是,当数据从远程服务器返回时,其他正在进行下载的iPhone线程不能在你的方法正在调用的同时调用你的对象,它是把消息放入你的应用event loop中。假如它直接调用了你的应用程序,可能恰好你的应用程序正在运行UI代码或者其他一些事情,那么你就不得不去编写线程安全的代码。所以数据到达的调用是作为一个事件存在于event loop中。event loop中的事件是运行在一个单线程中,每次执行一个事件。利用这些,我们可以从Flickr那异步的加载图片而不需要我们自己编写线程安全的代码。更好的是,Cocoa的URL加载系统将并行的加载这些URL。
但是,我们怎么在加载完图片后更新UITableViewCell了?UIImage是不可变,也就是说,当你加载完一个图片后就不能改变图片的数据了。值得庆幸的是苹果使得这项工作变得很简单。将UIView对象放入UITableViewCell中,而不是直接放入UIImage。开始时,你的View Object可以为空,或者它可以有一个虚假的图片在里面,或者你可以展示一些“something is happening”视图。当图片数据已经加载完全,创建一个UIImageView,设置图片,然后将它放入table cell中。
作者把这些功能封装成了一个类(AsyncImageView),具体代码如下。使用很方便,只需如下几个步骤:
1、alloc and initWithRect
2、add it to a view, eg in a table cell's content view;
3、send it the loadImageFromURL: message.
@interface AsyncImageView : UIView { NSURLConnection* connection; NSMutableData* data; } @end @implementation AsyncImageView - (void)loadImageFromURL:(NSURL*)url { if (connection!=nil) { [connection release]; } if (data!=nil) { [data release]; } NSURLRequest* request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0]; connection = [[NSURLConnection alloc] initWithRequest:request delegate:self]; //TODO error handling, what if connection is nil? } - (void)connection:(NSURLConnection *)theConnection didReceiveData:(NSData *)incrementalData { if (data==nil) { data = [[NSMutableData alloc] initWithCapacity:2048]; } [data appendData:incrementalData]; } - (void)connectionDidFinishLoading:(NSURLConnection*)theConnection { [connection release]; connection=nil; if ([[self subviews] count]>0) { [[[self subviews] objectAtIndex:0] removeFromSuperview]; } UIImageView* imageView = [[[UIImageView alloc] initWithImage:[UIImage imageWithData:data]] autorelease]; imageView.contentMode = UIViewContentModeScaleAspectFit; imageView.autoresizingMask = ( UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight ); [self addSubview:imageView]; imageView.frame = self.bounds; [imageView setNeedsLayout]; [self setNeedsLayout]; [data release]; data=nil; } - (UIImage*) image { UIImageView* iv = [[self subviews] objectAtIndex:0]; return [iv image]; } - (void)dealloc { [connection cancel]; [connection release]; [data release]; [super dealloc]; } @end
利用上面封装的一个类,可以编写一个UITableViewCell使用例子。代码如下。The AsyncImageView gets tagged with 999, and when it gets recycled, that 999 tagged view gets fished out and removed. So only the cell is being recycled, not the AsyncImageView object. When its removed from the cells content view it also gets released, causing dealloc, which in turn cancels the url download (if its outstanding).
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"ImageCell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease]; } else { AsyncImageView* oldImage = (AsyncImageView*) [cell.contentView viewWithTag:999]; [oldImage removeFromSuperview]; } CGRect frame; frame.size.width=75; frame.size.height=75; frame.origin.x=0; frame.origin.y=0; AsyncImageView* asyncImage = [[[AsyncImageView alloc] initWithFrame:frame] autorelease]; asyncImage.tag = 999; NSURL* url = [imageDownload thumbnailURLAtIndex:indexPath.row]; [asyncImage loadImageFromURL:url]; [cell.contentView addSubview:asyncImage]; return cell; }
相关文章推荐
- Android实战简易教程<四十九>(两种方式实现网络图片异步加载)
- cocos2d-x中CCTextureCache图片资源的异步加载<转>
- <img src="">标签通过js异步加载图片
- display不能解决<jsp:include />的问题,jsp异步加载另外一个jsp页面
- ios UITableView 异步加载图片并防止错位
- HTML5 <script>元素async,defer异步加载
- UITableView 重用 UITableViewCell 并异步加载图片时会出现图片错乱的情况的原因
- ios UITableView封装之下拉-上提-图片异步加载
- ios UITableView封装之下拉-上提-图片异步加载
- 我的Android进阶之旅------>android异步加载图片显示,并且对图片进行缓存实例
- ios UITableView封装之下拉-上提-图片异步加载 .
- iOS UITableView中异步加载图片 - 解决方案
- iOS网络编程(三) 异步加载及缓存图片---->SDWebImage
- 图片加载框架简单介绍<三> Glide的基本使用
- iOS UItableview加载图片的时候的优化之lazy(懒加载)模式and异步加载模式
- HTML5 <script>元素async,defer异步加载
- ios UITableView封装之下拉-上提-图片异步加载 .
- ios UITableView 异步加载图片并防止错位
- android,微信,人人,<android 无标题栏 >微博开机加载一幅图片,再跳转到主应用的实现
- HTML5 <script>元素async,defer异步加载