你好,歡迎來到IOS教程網

 Ios教程網 >> IOS訊息 >> 關於IOS >> tableview加載圖片的時候的優化之lazy(懶加載)模式and異步加載模式

tableview加載圖片的時候的優化之lazy(懶加載)模式and異步加載模式

編輯:關於IOS

舉個例子,當我們在用網易新聞App時,看著那麼多的新聞,並不是所有的都是我們感興趣的,有的時候我們只是很快的滑過,想要快速的略過不喜歡的內容,但是只要滑動經過了,圖片就開始加載了,這樣用戶體驗就不太好,而且浪費內存.

這個時候,我們就可以利用lazy加載技術,當界面滑動或者滑動減速的時候,都不進行圖片加載,只有當用戶不再滑動並且減速效果停止的時候,才進行加載.

剛開始我異步加載圖片利用SDWebImage來做,最後試驗的時候出現了重用bug,因為雖然SDWebImage實現了異步加載緩存,當加載完圖片後再請求會直接加載緩存中的圖片,注意注意注意,關鍵的來了,如果是lazy加載,滑動過程中是不進行網絡請求的,cell上的圖片就會發生重用,當你停下來能進行網絡請求的時候,才會變回到當前Cell應有的圖片,大概1-2秒的延遲吧(不算延遲,就是沒有進行請求,也不是沒有緩存的問題).怎麼解決呢?這個時候我們就要在Model對象中定義個一個UIImage的屬性,異步下載圖片後,用已經緩存在沙盒中的圖片路徑給它賦值,這樣,才cellForRowAtIndexPath方法中,判斷這個UIImage對象是否為空,若為空,就進行網絡請求,不為空,就直接將它賦值給cell的imageView對象,這樣就能很好的解決圖片短暫重用問題.

@下面我的代碼用的是自己寫的異步加載緩存類,SDWebImage的加載圖片的懶加載,原理差不多.

[objc] view plaincopytableview加載圖片的時候的優化之lazy(懶加載)模式and異步加載模式派生到我的代碼片

  1. @model類
  2. #import <Foundation/Foundation.h>
  3. @interface NewsItem : NSObject
  4. @property (nonatomic,copy) NSString * newsTitle;
  5. @property (nonatomic,copy) NSString * newsPicUrl;
  6. @property (nonatomic,retain) UIImage * newsPic; //  存儲每個新聞自己的image對象
  7. - (id)initWithDictionary:(NSDictionary *)dic;
  8. //  處理解析
  9. + (NSMutableArray *)handleData:(NSData *)data;
  10. @end
  11. #import "NewsItem.h"
  12. #import "ImageDownloader.h"
  13. @implementation NewsItem
  14. - (void)dealloc
  15. {
  16.     self.newsTitle = nil;
  17.     self.newsPicUrl = nil;
  18.     self.newsPic = nil;
  19.     [super dealloc];
  20. }
  21. - (id)initWithDictionary:(NSDictionary *)dic
  22. {
  23.     self = [super init];
  24.     if (self) {
  25.         self.newsTitle = [dic objectForKey:@"title"];
  26.         self.newsPicUrl = [dic objectForKey:@"picUrl"];
  27.         //從本地沙盒加載圖像
  28.         ImageDownloader * downloader = [[[ImageDownloader alloc] init] autorelease];
  29.         self.newsPic = [downloader loadLocalImage:_newsPicUrl];
  30.     }
  31.     return self;
  32. }
  33. + (NSMutableArray *)handleData:(NSData *)data;
  34. {
  35.         //解析數據
  36.         NSError * error = nil;
  37.         NSDictionary * dic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
  38.         NSMutableArray * originalArray = [dic objectForKey:@"news"];
  39.         //封裝數據對象
  40.         NSMutableArray * resultArray = [NSMutableArray array];
  41.         for (int i=0 ;i<[originalArray count]; i++) {
  42.             NSDictionary * newsDic = [originalArray objectAtIndex:i];
  43.             NewsItem * item = [[NewsItem alloc] initWithDictionary:newsDic];
  44.             [resultArray addObject:item];
  45.             [item release];
  46.         }
  47.         return resultArray;
  48. }
  49. @end

 

[objc] view plaincopytableview加載圖片的時候的優化之lazy(懶加載)模式and異步加載模式派生到我的代碼片

  1. @圖片下載類
  2. #import <Foundation/Foundation.h>
  3. @class NewsItem;
  4. @interface ImageDownloader : NSObject
  5. @property (nonatomic,copy) NSString * imageUrl;
  6. @property (nonatomic,retain) NewsItem * newsItem; //下載圖像所屬的新聞
  7. //圖像下載完成後,使用block實現回調
  8. @property (nonatomic,copy) void (^completionHandler)(void);
  9. //開始下載圖像
  10. - (void)startDownloadImage:(NSString *)imageUrl;
  11. //從本地加載圖像
  12. - (UIImage *)loadLocalImage:(NSString *)imageUrl;
  13. @end
  14. #import "ImageDownloader.h"
  15. #import "NewsItem.h"
  16. @implementation ImageDownloader
  17. - (void)dealloc
  18. {
  19.     self.imageUrl = nil;
  20.     Block_release(_completionHandler);
  21.     [super dealloc];
  22. }
  23. #pragma mark - 異步加載
  24. - (void)startDownloadImage:(NSString *)imageUrl
  25. {
  26.     self.imageUrl = imageUrl;
  27.     // 先判斷本地沙盒是否已經存在圖像,存在直接獲取,不存在再下載,下載後保存
  28.     // 存在沙盒的Caches的子文件夾DownloadImages中
  29.     UIImage * image = [self loadLocalImage:imageUrl];
  30.     if (image == nil) {
  31.         // 沙盒中沒有,下載
  32.         // 異步下載,分配在程序進程缺省產生的並發隊列
  33.         dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  34.             // 多線程中下載圖像
  35.             NSData * imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:imageUrl]];
  36.             // 緩存圖片
  37.             [imageData writeToFile:[self imageFilePath:imageUrl] atomically:YES];
  38.             // 回到主線程完成UI設置
  39.             dispatch_async(dispatch_get_main_queue(), ^{
  40.                 //將下載的圖像,存入newsItem對象中
  41.                 UIImage * image = [UIImage imageWithData:imageData];
  42.                 self.newsItem.newsPic = image;
  43.                 //使用block實現回調,通知圖像下載完成
  44.                 if (_completionHandler) {
  45.                     _completionHandler();
  46.                 }
  47.             });
  48.         });
  49.     }
  50. }
  51. #pragma mark - 加載本地圖像
  52. - (UIImage *)loadLocalImage:(NSString *)imageUrl
  53. {
  54.     self.imageUrl = imageUrl;
  55.     // 獲取圖像路徑
  56.     NSString * filePath = [self imageFilePath:self.imageUrl];
  57.     UIImage * image = [UIImage imageWithContentsOfFile:filePath];
  58.     if (image != nil) {
  59.         return image;
  60.     }
  61.     return nil;
  62. }
  63. #pragma mark - 獲取圖像路徑
  64. - (NSString *)imageFilePath:(NSString *)imageUrl
  65. {
  66.     // 獲取caches文件夾路徑
  67.     NSString * cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
  68.     // 創建DownloadImages文件夾
  69.     NSString * downloadImagesPath = [cachesPath stringByAppendingPathComponent:@"DownloadImages"];
  70.     NSFileManager * fileManager = [NSFileManager defaultManager];
  71.     if (![fileManager fileExistsAtPath:downloadImagesPath]) {
  72.         [fileManager createDirectoryAtPath:downloadImagesPath withIntermediateDirectories:YES attributes:nil error:nil];
  73.     }
  74. #pragma mark 拼接圖像文件在沙盒中的路徑,因為圖像URL有"/",要在存入前替換掉,隨意用"_"代替
  75.     NSString * imageName = [imageUrl stringByReplacingOccurrencesOfString:@"/" withString:@"_"];
  76.     NSString * imageFilePath = [downloadImagesPath stringByAppendingPathComponent:imageName];
  77.     return imageFilePath;
  78. }
  79. @end

 

[objc] view plaincopytableview加載圖片的時候的優化之lazy(懶加載)模式and異步加載模式派生到我的代碼片

  1. @這裡只給出關鍵代碼,網絡請求,數據處理,自定義cell自行解決
  2. #pragma mark - Table view data source
  3. - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
  4. {
  5.     // Return the number of sections.
  6.     return 1;
  7. }
  8. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
  9. {
  10.     // Return the number of rows in the section.
  11.     if (_dataArray.count == 0) {
  12.         return 10;
  13.     }
  14.     return [_dataArray count];
  15. }
  16. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
  17. {
  18.     static NSString *cellIdentifier = @"Cell";
  19.     NewsListCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier ];
  20.     if (!cell) {
  21.         cell = [[[NewsListCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier] autorelease];
  22.     }
  23.     NewsItem * item = [_dataArray objectAtIndex:indexPath.row];
  24.     cell.titleLabel.text = item.newsTitle;
  25.     //判斷將要展示的新聞有無圖像
  26.     if (item.newsPic == nil) {
  27.         //沒有圖像下載
  28.         cell.picImageView.image = nil;
  29.         NSLog(@"dragging = %d,decelerating = %d",self.tableView.dragging,self.tableView.decelerating);
  30.         // ??執行的時機與次數問題
  31.         if (self.tableView.dragging == NO && self.tableView.decelerating == NO) {
  32.             [self startPicDownload:item forIndexPath:indexPath];
  33.         }
  34.     }else{
  35.         //有圖像直接展示
  36.         NSLog(@"1111");
  37.         cell.picImageView.image = item.newsPic;
  38.     }
  39.     cell.titleLabel.text = [NSString stringWithFormat:@"indexPath.row = %ld",indexPath.row];
  40.     return cell;
  41. }
  42. - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
  43. {
  44.     return [NewsListCell cellHeight];
  45. }
  46. //開始下載圖像
  47. - (void)startPicDownload:(NewsItem *)item forIndexPath:(NSIndexPath *)indexPath
  48. {
  49.     //創建圖像下載器
  50.     ImageDownloader * downloader = [[ImageDownloader alloc] init];
  51.     //下載器要下載哪個新聞的圖像,下載完成後,新聞保存圖像
  52.     downloader.newsItem = item;
  53.     //傳入下載完成後的回調函數
  54.     [downloader setCompletionHandler:^{
  55.         //下載完成後要執行的回調部分,block的實現
  56.         //根據indexPath獲取cell對象,並加載圖像
  57. #pragma mark cellForRowAtIndexPath-->沒看到過
  58.         NewsListCell * cell = (NewsListCell *)[self.tableView cellForRowAtIndexPath:indexPath];
  59.         cell.picImageView.image = downloader.newsItem.newsPic;
  60.     }];
  61.     //開始下載
  62.     [downloader startDownloadImage:item.newsPicUrl];
  63.     [downloader release];
  64. }
  65. - (void)loadImagesForOnscreenRows
  66. {
  67. #pragma mark indexPathsForVisibleRows-->沒看到過
  68.     //獲取tableview正在window上顯示的cell,加載這些cell上圖像。通過indexPath可以獲取該行上需要展示的cell對象
  69.     NSArray * visibleCells = [self.tableView indexPathsForVisibleRows];
  70.     for (NSIndexPath * indexPath in visibleCells) {
  71.         NewsItem * item = [_dataArray objectAtIndex:indexPath.row];
  72.         if (item.newsPic == nil) {
  73.             //如果新聞還沒有下載圖像,開始下載
  74.             [self startPicDownload:item forIndexPath:indexPath];
  75.         }
  76.     }
  77. }
  78. #pragma mark - 延遲加載關鍵
  79. //tableView停止拖拽,停止滾動
  80. - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
  81. {
  82.     //如果tableview停止滾動,開始加載圖像
  83.     if (!decelerate) {
  84.         [self loadImagesForOnscreenRows];
  85.     }
  86.      NSLog(@"%s__%d__|%d",__FUNCTION__,__LINE__,decelerate);
  87. }
  88. - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
  89. {
  90.     //如果tableview停止滾動,開始加載圖像
  91.     [self loadImagesForOnscreenRows];
  92. }

 

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved