很早以前就看过《LazyTableImages》的代码,当时只是大致看了一下它的原理,没有很详细的研读。最近在看第三方开源框架的代码,学习优化策略以及优雅的代码风格,提高自身水平,希望与大家一起学习和共同讨论。
一、简要流程说明。
1、通过RSS feed建立URL请求获取XML数据;
2、通过获取的XML数据,创建ParseOperation在后台使用NSXMLParser运行解析XML数据得到想要的数据存放在NSArray里面;
3、ParseOperation执行完后告诉table view在主线程上面重新加载数据;
4、当UITableViewCell不再滚动或拖动的时候,通过可见cells的indexPath在NSArray里面得到cells对应的每一个imageURLString,为每一个imageURLString创建一个IconDownloader下载图片,每一个IconDownloader下载完毕通过委托的方式为cell更新image。
二、详细流程讲解:
1.利用NSOperation队列异步解析RSS,ParseOperation执行完成,通知主线程重新加载数据,更新UI,主要代码如下:
NSOperation的相关知识可以查看http://blog.csdn.net/jasonjwl/article/details/50992720
_parser = [[ParseOperation alloc] initWithData:data]; __weak LazyTableAppDelegate *weakSelf = self; //避免循环引用的常用做法 self.parser.completionBlock = ^(void) { if (weakParser.appRecordList != nil) { //确保主线程更新UI dispatch_async(dispatch_get_main_queue(), ^{ RootViewController *rootViewController = (RootViewController*)[(UINavigationController*)weakSelf.window.rootViewController topViewController]; rootViewController.entries = weakParser.appRecordList; //当解析完成后告诉table view刷新数据 [rootViewController.tableView reloadData]; }); } // 当解析完成后,将队列置为nil weakSelf.queue = nil; }; [self.queue addOperation:self.parser]; // 开始执行解析1.1 ParseOperation开始解析,会调用文件ParseOperation.m中的main方法,将解析后的结果存储在appRecordList数组
- (void)main { _workingArray = [NSMutableArray array]; _workingPropertyString = [NSMutableString string]; //利用NSXMLParser解析XML NSXMLParser *parser = [[NSXMLParser alloc] initWithData:self.dataToParse]; [parser setDelegate:self]; [parser parse]; if (![self isCancelled]) { // 将解析结果保存在appRecordList self.appRecordList = [NSArray arrayWithArray:self.workingArray]; } self.workingArray = nil; self.workingPropertyString = nil; self.dataToParse = nil; }2.解析XML完成后,赋值rootViewController.entries = weakParser.appRecordList;通过Block回调通知rootViewController.tableView进行更新。
2.1 在加载cell的时候,做了相应优化 ,如下所示:
当没有缓存images,只有等滚动停止的时候才会让下载器下载图片
如果cell的image正在下载,则返回占位图片
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = nil; NSUInteger nodeCount = self.entries.count; if (nodeCount == 0 && indexPath.row == 0) { // add a placeholder cell while waiting on table data //当等待cell加载的时候,添加占位cell cell = [tableView dequeueReusableCellWithIdentifier:PlaceholderCellIdentifier forIndexPath:indexPath]; } else { //重用cell cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath]; if (nodeCount > 0) { //将每一行cell跟app关联起来 AppRecord *appRecord = (self.entries)[indexPath.row]; cell.textLabel.text = appRecord.appName; cell.detailTextLabel.text = appRecord.artist; //当没有缓存images,只有等滚动停止的时候才会让下载器下载图片 if (!appRecord.appIcon) { if (self.tableView.dragging == NO && self.tableView.decelerating == NO) { [self startIconDownload:appRecord forIndexPath:indexPath]; } //如果cell的image正在下载,则返回占位图片 cell.imageView.image = [UIImage imageNamed:@"Placeholder.png"]; } else { cell.imageView.image = appRecord.appIcon; } } } return cell; }2.2 只有当滚动停止时才加载可见cell的图片
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate { if (!decelerate) { [self loadImagesForOnscreenRows]; } }2.3 当图片已下载完成时,要避免重复下载
- (void)loadImagesForOnscreenRows { if (self.entries.count > 0) { NSArray *visiblePaths = [self.tableView indexPathsForVisibleRows]; for (NSIndexPath *indexPath in visiblePaths) { AppRecord *appRecord = (self.entries)[indexPath.row]; if (!appRecord.appIcon) // Avoid the app icon download if the app already has an icon { [self startIconDownload:appRecord forIndexPath:indexPath]; } } } }
2.除了上述提了的一些优化cell方法,还有那些优化cell的方法。
欢迎大家交流讨论!如有不对的地方,请指出,谢谢!本人正在找iOS方面的工作,如有合适的职位请跟我联系,谢谢!
后续我将带来MJExtension和SDWebImage源码解析系列
LazyTableImages的下载地址:https://developer.apple.com/library/ios/samplecode/LazyTableImages/Introduction/Intro.html