uitable,UITables With Downloaded Images - Easy Asynchronous Code <UITable 异步加载图片>

本文翻译来自:http://www.markj.net/iphone-asynchronous-table-image/
在开发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产生回调。
iPhone_App_RuntimeUITables With Downloaded Images - Easy Asynchronous Code <UITable 异步加载图片>uitable
根据以上描述,在开发中就可以编写如下简单代码:
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 _disibledevent=> - (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; }
Tags:  easyboot uitable

延伸阅读

最新评论

发表评论