IOS --- optimize lazy (lazy) mode and asynchronous loading mode when tableview loads images,
For example, when we use the Netease news App and watch so many news, not all of them are of interest to us. Sometimes we just slide fast, if you want to quickly skip the content you don't like, but as long as you slide it, the image starts to be loaded. In this way, the user experience is not good and the memory is wasted.
At this time, we can use lazy loading technology. When the interface slides or slides to slow down, images are not loaded. Only when the user no longer slides and the deceleration effect stops,.
At the beginning, I asynchronously Loaded Images Using SDWebImage to do this. In the end of the experiment, I encountered a reuse bug because although SDWebImage implements asynchronous loading of cache, after loading the image, the request will directly load the image in the cache. Note that the key is to load the image. For lazy loading, no network request is made during the slide process, the image on the cell will be reused. When you stop and make a network request, the image will be returned to the current Cell. The latency is about 1-2 seconds (no delay, that is, there is no request, and there is no cache problem ). how can this problem be solved? At this time, we need to define a UIImage attribute in the Model object. After downloading the image asynchronously, assign a value to the image path that has been cached in the sandbox. In this way, the cellForRowAtIndexPath method is used, determine whether the UIImage object is null. If it is null, a network request is made. If it is not null, it is directly assigned to the imageView object of the cell, in this way, we can solve the problem of temporary reuse of images.
@ The following code uses a self-written asynchronous loading cache class. The principle of loading images by SDWebImage is similar.
@ Model class # import <Foundation/Foundation. h> @ interface NewsItem: NSObject @ property (nonatomic, copy) NSString * newsTitle; @ property (nonatomic, copy) NSString * newsPicUrl; @ property (nonatomic, retain) UIImage * newsPic; // store the image object of each news item-(id) initWithDictionary :( NSDictionary *) dic; // process parsing + (NSMutableArray *) handleData :( NSData *) data; @ end # import "NewsItem. h "# import" ImageDownloader. h "@ implementation NewsItem-(void) dealloc {self. newsTitle = nil; self. newsPicUrl = nil; self. newsPic = nil; [super dealloc];}-(id) initWithDictionary :( NSDictionary *) dic {self = [super init]; if (self) {self. newsTitle = [dic objectForKey: @ "title"]; self. newsPicUrl = [dic objectForKey: @ "picUrl"]; // load the image ImageDownloader * downloader = [[[ImageDownloader alloc] init] autorelloader]; self. newsPic = [downloader loadLocalImage: _ newsPicUrl];} return self;} + (NSMutableArray *) handleData :( NSData *) data; {// parse data NSError * error = nil; NSDictionary * dic = [NSJSONSerialization JSONObjectWithData: data options: NSJSONReadingMutableContainers error: & error]; NSMutableArray * originalArray = [dic objectForKey: @ "news"]; // encapsulate the Data Object NSMutableArray * resultArray = [NSMutableArray array]; for (int I = 0; I <[originalArray count]; I ++) {NSDictionary * newsDic = [originalArray objectAtIndex: I]; NewsItem * item = [[NewsItem alloc] initWithDictionary: newsDic]; [resultArray addObject: item]; [item release];} return resultArray;} @ end
@ Image download class # import <Foundation/Foundation. h> @ class NewsItem; @ interface ImageDownloader: NSObject @ property (nonatomic, copy) NSString * imageUrl; @ property (nonatomic, retain) NewsItem * newsItem; // download the news of the image // After the image is downloaded, use block to implement callback @ property (nonatomic, copy) void (^ completionHandler) (void ); // start to download the image-(void) startDownloadImage :( NSString *) imageUrl; // load the image locally-(UIImage *) loadLocalImage :( NSString *) imageUrl; @ end # im Port "ImageDownloader. h "# import" NewsItem. h "@ implementation ImageDownloader-(void) dealloc {self. imageUrl = nil; Block_release (_ completionHandler); [super dealloc] ;}# pragma mark-asynchronous loading-(void) startDownloadImage :( NSString *) imageUrl {self. imageUrl = imageUrl; // first, judge whether an image already exists in the local sandbox. If an image already exists, you can directly obtain it and download it if it does not exist, after DownloadImages, save // UIImage * image = [self loadLocalImage: imageUrl] In the Caches subfolder of the sandbox; if (image = nil) {// in the sandbox No, download // asynchronous download, assigned to the default concurrent queue dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) generated by the program process ), ^ {// download image NSData * imageData = [NSData dataWithContentsOfURL: [NSURL URLWithString: imageUrl]; // cache image [imageData writeToFile: [self imageFilePath: imageUrl] atomically: YES]; // return to the main thread to complete UI setting dispatch_async (dispatch_get_main_queue (), ^ {// Save the downloaded image to the newsItem object, UIImage * image = [UIImage imageWithDa Ta: imageData]; self. newsItem. newsPic = image; // use block to implement callback and notify the image download to complete if (_ completionHandler) {_completionhandler ();}});});}} # pragma mark-load local image-(UIImage *) loadLocalImage :( NSString *) imageUrl {self. imageUrl = imageUrl; // obtain the image path NSString * filePath = [self imageFilePath: self. imageUrl]; UIImage * image = [UIImage imageWithContentsOfFile: filePath]; if (image! = Nil) {return image;} return nil;} # pragma mark-Get image path-(NSString *) imageFilePath :( NSString *) imageUrl {// obtain the caches folder path NSString * cachesPath = [canonical (NSCachesDirectory, NSUserDomainMask, YES) lastObject]; // create the DownloadImages folder NSString * downloadImagesPath = [cachesPath attributes: @ "DownloadImages"]; NSFileManager * fileManager = [NSFileManager defau LtManager]; if (! [FileManager fileExistsAtPath: downloadImagesPath]) {[fileManager createDirectoryAtPath: downloadImagesPath withIntermediateDirectories: YES attributes: nil error, because the image URL contains "/", you must replace it before saving it. Use "_" instead of NSString * imageName = [imageUrl stringByReplacingOccurrencesOfString: @ "/" withString: @ "_"]; NSString * imageFilePath = [downloadImagesPath stringByAppendingPathComponent: imageName]; return imageFilePath;} @ end
@ Here, only key code, network requests, data processing, and custom cell are provided. # pragma mark-Table view data source-(NSInteger) numberOfSectionsInTableView :( UITableView *) tableView {// Return the number of sessions. return 1;}-(NSInteger) tableView :( UITableView *) tableView numberOfRowsInSection :( NSInteger) section {// Return the number of rows in the section. if (_ dataArray. count = 0) {return 10;} return [_ dataArray count];}-(UITableViewCel L *) tableView :( UITableView *) tableView cellForRowAtIndexPath :( NSIndexPath *) indexPath {static NSString * cellIdentifier = @ "Cell"; NewsListCell * cell = [tableView progress: cellIdentifier]; if (! Cell) {cell = [[[NewsListCell alloc] initWithStyle: UITableViewCellStyleDefault reuseIdentifier: cellIdentifier] autorelease];} NewsItem * item = [_ dataArray objectAtIndex: indexPath. row]; cell. titleLabel. text = item. newsTitle; // determines whether the news to be displayed has an image if (item. newsPic = nil) {// no image to download cell. picImageView. image = nil; NSLog (@ "dragging = % d, decelerating = % d", self. tableView. dragging, self. tableView. decelerating );//?? Execution time and number of times if (self. tableView. dragging = NO & self. tableView. decelerating = NO) {[self startPicDownload: item forIndexPath: indexPath] ;}} else {// NSLog (@ "1111") directly displayed with images; cell. picImageView. image = item. newsPic;} cell. titleLabel. text = [NSString stringWithFormat: @ "indexPath. row = % ld ", indexPath. row]; return cell;}-(CGFloat) tableView :( UITableView *) tableView heightForRowAtIndexPath :( NSIndexPath *) indexPa Th {return [NewsListCell cellHeight];} // start to download the image-(void) startPicDownload :( NewsItem *) item forIndexPath :( NSIndexPath *) indexPath {// create the image download tool ImageDownloader * downloader = [[ImageDownloader alloc] init]; // download the image of the news file to be downloaded. After the download is complete, the news file stores the image downloader. newsItem = item; // input the callback function after the download is complete [downloader setCompletionHandler: ^ {// The callback part to be executed after the download is complete, block implementation // obtain the cell Object Based on indexPath and load the image # pragma mark cellForRowAtIndexPath --> NO NewsList seen Cell * cell = (NewsListCell *) [self. tableView cellForRowAtIndexPath: indexPath]; cell. picImageView. image = downloader. newsItem. newsPic;}]; // starts to download [downloader startDownloadImage: item. newsPicUrl]; [downloader release];}-(void) loadImagesForOnscreenRows {# pragma mark indexPathsForVisibleRows --> I have not seen // obtain the cell displayed on the window and load these cell images. You can use indexPath to obtain the cell Object NSArray * visibleCells = [self. tableView indexPathsForVisibleRows]; for (NSIndexPath * indexPath in visibleCells) {NewsItem * item = [_ dataArray objectAtIndex: indexPath. row]; if (item. newsPic = nil) {// if the news has not downloaded the image, download [self startPicDownload: item forIndexPath: indexPath] ;}} # pragma mark-Key to delayed loading // tableView stop dragging and stop rolling-(void) scrollViewDidEndDragging :( UIScrollView *) scrollView WillDecelerate :( BOOL) decelerate {// if tableview stops scrolling, start loading the image if (! Decelerate) {[self loadImagesForOnscreenRows];} NSLog (@ "% s __% d __| % d" ,__ FUNCTION __,__ LINE __, decelerate);}-(void) scrollViewDidEndDecelerating :( UIScrollView *) scrollView {// If tableview stops scrolling, start loading the image [self loadImagesForOnscreenRows];}