iOS 網絡圖片優化

概述:

iOS 開發中,很多app的網絡圖片處理使用SDwebImage或YYKit的YYImage,使用方便、穩定、內部做了很多的優化處理。但也存在一些問題

1、SDWebImage庫提供了一整套的網絡圖片異步下載和緩存機制,還增加了UIImageView、UIButton等的Category,方便顯示網絡圖片。

2、存在的問題
由于網絡圖片一般不會有@2x和@3x之分,通過SDWebImage庫下載的圖片不加以處理就直接顯示,會有一些常見的問題,如像素不對齊。
App中經常使用圓角圖片,一般采用裁剪圖片的方式;但是這些圖片源來自服務器(本地圓角圖片讓UI直接提供就可以了),我們需要在SDWebImage基礎上增加對網絡圓角圖片的處理。

GPU圖層顯示的優化

參考

一、像素不對齊
檢測方式:

以下兩種方式均可發現存在Misaligned Images問題的地方:

  • 模擬器調試時,打開模擬器的Debug - Color Misaligned Images菜單選項。最快捷,但僅限模擬器上查看。
  • Instrument性能檢測時,選中Core Animation模板,在Display Settings中勾選Color Misaligned Images選項。可針對模擬器和真機,可查看真機上所有應用的像素混合情況。
問題定義:

打開開關后,看到部分視圖會有黃色或洋紅色(Magenta)的圖層標記,代表其像素不對齊。

  • 不對齊:視圖或圖片的點數(point),不能換算成整數的像素值(pixel),導致顯示視圖的時候需要對沒對齊的邊緣進行額外混合計算,影響性能。
  • 洋紅色:UIView的frame像素不對齊,即不能換算成整數像素值。
  • 黃色:UIImageView的圖片像素大小與其frame.size不對齊,圖片發生了縮放造成。
優化方式:

frame像素不對齊

借助ceilf()、floorf()、CGRectIntegral()等將小數點后數據除去即可。
使用floorf時,需要注意是否會因為向下取整而影響視圖的顯示。
像素不對稱齊的元素一般為UILabel或UIImageView。

圖片像素不對齊

圖片是從服務端獲取到的,大小不規則。直接在UIImageView上顯示容易出現像素不對齊。

解決方法

將下載到的圖片,縮放到與UIImageView對應的尺寸,再顯示出來。

#import "UIImage+Additions.h"

/**
 圖片縮放 避免圖片像素不對齊
 需要縮放圖片到與UIImageView對應的尺寸,且縮放后的圖片的scale和[UIScreen mainScreen].scale相等,再顯示出來。
 圖片的縮放是相對耗時的,放在非主線程,更新圖片在主線程
 @param size 要達到的尺寸 一般為UIImageView的size
 @return 縮放后的圖片
 */
- (UIImage *)scaleImageWithSize:(CGSize)size{
    
    if (CGSizeEqualToSize(size, self.size)) {
        return self;
    }
    //創建上下文
    UIGraphicsBeginImageContextWithOptions(size, YES, [UIScreen mainScreen].scale);
    //繪圖
    [self drawInRect:CGRectMake(0, 0, size.width, size.height)];
    //獲取新圖片
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return newImage;
}

2、像素混合
像素混合是指在某視圖為透明背景色,GPU在渲染視圖時,需要將該視圖和下層視圖混合(Blend)后才能計算出該像素點的實際顏色;這增加了GPU的工作,損耗了性能。
當圖片是透明圖片時,像素混合必然會發生。所以顯示的圖片最好是不透明的。
iPhone模擬器中的Debug ->Color Blended Layers選項 和 Core Animation ->Display Settings ->Color Blended Layers都可以將像素混合的部分顯示出來。
發生了像素混合的區域顯示紅色,正常則顯示綠色。

圖片處理使用Core Graphics實現。在使用UIGraphicsBeginImageContextWithOptions創建上下文時候,opaque默認為YES,表示不透明;

3、圓角圖片的問題
不建議的方案1:通過設置cornerRadius值和masksToBounds=YES實現圓角效果。因為它會觸發GPU的離屏渲染,引起性能問題。模擬器中的Color Offscreen-Rendered可以檢測是否發生離屏渲染(如果出現黃色就發生了離屏渲染)。
不建議的方案2:通過設置view.layer的mask屬性,將另一個layer蓋在view上,也可以實現圓角的效果,但是同樣會觸發離屏渲染,引起性能問題。
建議的方案:使用Core Graphics重新繪制帶圓角的圖片,雖然在顯示上提升了性能,但是增加了繪制的工作,所以要做好異步繪制和緩存工作,盡可能避免重復繪制。使用SD的UIImage+Transform分類方法:

#import<UIImage+Transform.h>
- (nullable UIImage *)sd_roundedCornerImageWithRadius:(CGFloat)cornerRadius
                                              corners:(SDRectCorner)corners
                                          borderWidth:(CGFloat)borderWidth
                                          borderColor:(nullable UIColor *)borderColor;

二、內存暴增

使用SDWebImage和YYImage下載高分辨率圖時,可能導致內存暴增

WeChatf1ddd36fc515c2a32895eacb8bf38b22.png

用instrument檢測發現是SDWebImage和YYImage對圖像進行解壓縮操作時引起的,那么這兩個框架使用CGBitmapContextCreate的目的是為了優化圖片加載或者從本地加載圖片后的解碼過程,但decodeImageWithImage這個方法用于對圖片進行解壓縮并且緩存起來,以保證tableviews/collectionviews 交互更加流暢,但是如果是加載高分辨率圖片的話,會適得其反,有可能造成上G的內存消耗。對于高分辨率的圖片,應該禁止解壓縮操作,相關的代碼處理為:

解決方式一(推薦):設置SDWebImageOptions
- (void)sd_setImageWithURL:(nullable NSURL *)url
          placeholderImage:(nullable UIImage *)placeholder
                   options:(SDWebImageOptions)options
image.png
解決方式二:SDWebImage5.0+已經沒有此方法
#import <SDWebImage/SDImageCache.h>
#import <SDWebImage/SDWebImageDownloader.h>
// 禁用解壓縮
- (void)loadView{
    [[SDImageCache sharedImageCache].config setShouldDecompressImages:NO];
    [[SDWebImageDownloader sharedDownloader] setShouldDecompressImages:NO];
}
//恢復原設置
- (void)dealloc{
    [[SDImageCache sharedImageCache].config setShouldDecompressImages:YES];
    [[SDWebImageDownloader sharedDownloader] setShouldDecompressImages:YES];
}

此方式既能保證高分辨率圖不crash,也能保證其他地方,普通圖片依舊可以通過解壓縮進行優化。

另外在收到內存警告時,做如下操作:

- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application{
    // 清空緩存(內存)
    [[SDImageCache sharedImageCache] clearMemory];
    // 清空磁盤圖片
    [[SDImageCache sharedImageCache] clearDiskOnCompletion:nil];
}
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // 清空緩存(內存)
    [[SDImageCache sharedImageCache] clearMemory];
    // 清空磁盤圖片
    [[SDImageCache sharedImageCache] clearDiskOnCompletion:nil];
    // Dispose of any resources that can be recreated.
}

解決方式三:

參考

使用SDWebimage肯定都會做三件事,一判斷本地是否有這張圖,二有的時候直接從本地取圖片,三沒有的時候去網絡下載。在內部都會使用到下面這個方法

image.png

image.png

發現這里面對圖片的處理是直接按照原大小進行的,如果幾千是分辨率這里導致占用了大量內存,所以我們需要在這里對圖片做一次等比的壓縮。在UIImage+MultiFormat這個類里面添加如下壓縮方法,


+(UIImage *)compressImageWith:(UIImage *)image
{
    float imageWidth = image.size.width;
    float imageHeight = image.size.height;
    float width = 640;
    float height = image.size.height/(image.size.width/width);
    
    float widthScale = imageWidth /width;
    float heightScale = imageHeight /height;
    
    // 創建一個bitmap的context
    // 并把它設置成為當前正在使用的context
    UIGraphicsBeginImageContext(CGSizeMake(width, height));
    
    if (widthScale > heightScale) {
        [image drawInRect:CGRectMake(0, 0, imageWidth /heightScale , height)];
    }
    else {
        [image drawInRect:CGRectMake(0, 0, width , imageHeight /widthScale)];
    }
    
    // 從當前context中創建一個改變大小后的圖片
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    // 使當前的context出堆棧
    UIGraphicsEndImageContext();
    
    return newImage;
    
}

再在上面 UIImage *image = [[UIImage alloc] initWithData:data];代碼后面對圖片進行壓縮

UIImage *image = [[UIImage alloc] initWithData:data];
if (data.length/1024 > 128) {
       image = [self compressImageWith:image];
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。