1. 程式人生 > >iOS【完美解決SDWebImage載入多個圖片記憶體崩潰的問題】

iOS【完美解決SDWebImage載入多個圖片記憶體崩潰的問題】

SDWebImage大家肯定都恨熟悉了,國內外太多的App使用其進行圖片載入。

但是最近在使用過程中發現,我用SDWebImage載入多個圖片,類似微博動態那種,在載入的過程中。我發現當圖片解析度比較大的時候(不是圖片大),載入幾張圖片就崩潰了。

網上說可以每次載入圖片清空memcache,但是效果並不好。

 [[SDImageCache sharedImageCache] setValue:nil forKey:@"memCache"];

也有說把使用下面這個方法的地方全部注掉

+ (UIImage *)decodedImageWithImage:(UIImage *)image 

但是效果並不明顯。同時載入5-7張高解析度圖片還是會立即崩潰

我們使用SDWebimage肯定都會做三件事,一判斷本地是否有這張圖,二有的時候直接從本地取圖片,三沒有的時候去網路下載。

大概是像下面這樣

  1. NSString *logoString = [_currentDic stringValueForKey:@"team_img"];  
  2.    if(logoString.length>0){  
  3.    [[SDImageCache sharedImageCache] queryDiskCacheForKey:logoString done:^(UIImage *image, SDImageCacheType cacheType) {  
  4.        if
     (image) {  
  5.            [_teamImage setImage:image];  
  6.        }else{  
  7.            [_teamImage sd_setImageWithURL:kNSUrl(logoString)  
  8.                          placeholderImage:IMGNAMED(@"defaultAvatar2")  
  9.                                   options:SDWebImageRefreshCached  
  10.                                 completed
    :^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {  
  11.                                     if (image) {  
  12.                                         [[SDImageCache sharedImageCache] storeImage:image forKey:logoString toDisk:YES];  
  13.                                     }  
  14.                                 }];  
  15.        }  
  16.        }];}  

在內部都會使用到下面這個方法
  1. - (UIImage *)diskImageForKey:(NSString *)key {  
  2.     NSData *data = [self diskImageDataBySearchingAllPathsForKey:key];  
  3.     if (data) {  
  4.         UIImage *image = [UIImage sd_imageWithData:data];  
  5.         image = [self scaledImageForKey:key image:image];  
  6.         image = [UIImage decodedImageWithImage:image];  
  7.         return image;  
  8.     }  
  9.     else {  
  10.         return nil;  
  11.     }  
  12. }  

我發現這裡
  1. UIImage *image = [UIImage sd_imageWithData:data];  

圖片取出來的時候就已經巨大無比,佔用了很大的記憶體,導致記憶體來不及釋放就崩潰。

抽絲剝繭我們進入

  1. sd_imageWithData方法  

發現這裡面對圖片的處理是直接按照原大小進行的,如果幾千是解析度這裡導致佔用了大量記憶體。


所以我們需要在這裡對圖片做一次等比的壓縮。

我們在

UIImage+MultiFormat這個類裡面新增如下壓縮方法,

  1. +(UIImage *)compressImageWith:(UIImage *)image  
  2. {  
  3.     float imageWidth = image.size.width;  
  4.     float imageHeight = image.size.height;  
  5.     float width = 640;  
  6.     float height = image.size.height/(image.size.width/width);  
  7.     float widthScale = imageWidth /width;  
  8.     float heightScale = imageHeight /height;  
  9.     // 建立一個bitmap的context
  10.     // 並把它設定成為當前正在使用的context
  11.     UIGraphicsBeginImageContext(CGSizeMake(width, height));  
  12.     if (widthScale > heightScale) {  
  13.         [image drawInRect:CGRectMake(00, imageWidth /heightScale , height)];  
  14.     }  
  15.     else {  
  16.         [image drawInRect:CGRectMake(00, width , imageHeight /widthScale)];  
  17.     }  
  18.     // 從當前context中建立一個改變大小後的圖片
  19.     UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();  
  20.     // 使當前的context出堆疊
  21.     UIGraphicsEndImageContext();  
  22.     return newImage;  
  23. }  
再在上面箭頭程式碼後面對圖片進行壓縮
  1. #ifdef SD_WEBP
  2.     elseif ([imageContentType isEqualToString:@"image/webp"])  
  3.     {  
  4.         image = [UIImage sd_imageWithWebPData:data];  
  5.     }  
  6. #endif
  7.     else {  
  8.         image = [[UIImage alloc] initWithData:data];  
  9.         if (data.length/1024 > 128) {  
  10.             image = [self compressImageWith:image];  
  11.         }  
  12.         UIImageOrientation orientation = [self sd_imageOrientationFromImageData:data];  
  13.         if (orientation != UIImageOrientationUp) {  
  14.             image = [UIImage imageWithCGImage:image.CGImage
  15.                                         scale:image.scale
  16.                                   orientation:orientation];  
  17.         }  
到了這裡還需要進行最後一步。就是在SDWebImageDownloaderOperation的connectionDidFinishLoading方法裡面的:

UIImage *image = [UIImage sd_imageWithData:self.imageData];

//將等比壓縮過的image在賦在轉成data賦給self.imageData
NSData *data = UIImageJPEGRepresentation(image, 1);
self.imageData = [NSMutableData dataWithData:data];

   再配合    [[SDImageCachesharedImageCachesetValue:nilforKey:@"memCache"];(圖片載入後使用)大功告成,親測記憶體基本變化不大,自動釋放也來得及。