SDWebImage 對(duì)多gif顯示內(nèi)存消耗過高的優(yōu)化

內(nèi)存過高

  • 項(xiàng)目中有時(shí)候會(huì)遇到當(dāng)前頁(yè)面用到大量gif的情況,這個(gè)時(shí)候如果僅僅用SDWebImage去加載gif的話,會(huì)出現(xiàn)內(nèi)存暴增的現(xiàn)象.
  • 這是因?yàn)?SD在對(duì) gif 的處理過程中采用了一個(gè)數(shù)組存儲(chǔ) gif 的幀圖片,當(dāng)有大量動(dòng)態(tài)圖時(shí),大量圖片存在內(nèi)存中,造成了內(nèi)存暴增的現(xiàn)象.

原因分析

  • 先看SDWebImage的源代碼,SDWebImage通過這個(gè)類UIImage+GIF.h來處理gif,我們進(jìn)入頭文件發(fā)現(xiàn)會(huì)調(diào)用一個(gè)+ (UIImage *)sd_animatedGIFWithData:(NSData *)data 這樣的類方法
    下面是這個(gè)方法的源代碼,我已經(jīng)加了很詳細(xì)的注釋,并且把問題的所在也寫的很清楚.
+ (UIImage *)sd_animatedGIFWithData:(NSData *)data {
    //安全判斷
    if (!data) {
        return nil;
    }
    //二進(jìn)制類型的轉(zhuǎn)換
    //CGImageSourceRef是個(gè)什么呢? 我們可以看到這是一個(gè)typedef CGImageSource * CGImageSourceRef;
    //這是一個(gè)指針,CGImageSource是對(duì)圖像數(shù)據(jù)讀取任務(wù)的抽象,通過它可以獲得圖像對(duì)象、縮略圖、圖像的屬性(包括Exif信息)。
    CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);
    //獲取有幾張圖片
    size_t count = CGImageSourceGetCount(source);
    //返回的動(dòng)態(tài)圖片
    UIImage *animatedImage;
    //如果為一張圖片,那就只顯示一張圖片
    if (count <= 1) {
        animatedImage = [[UIImage alloc] initWithData:data];
    }
    //如果為多張圖片,就開始創(chuàng)建動(dòng)態(tài)圖片
    else {
        //集合 存放單張的圖片
        NSMutableArray *images = [NSMutableArray array];
        //時(shí)長(zhǎng)
        NSTimeInterval duration = 0.0f;
        for (size_t i = 0; i < count; i++) {
            //取出gif單張的圖片
            CGImageRef image = CGImageSourceCreateImageAtIndex(source, i, NULL);
            // 計(jì)算出單張圖片的播放時(shí)長(zhǎng)
            duration += [self sd_frameDurationAtIndex:i source:source];
            //添加到數(shù)組中
            [images addObject:[UIImage imageWithCGImage:image scale:[UIScreen mainScreen].scale orientation:UIImageOrientationUp]];
            //釋放
            CGImageRelease(image);
        }
        //安全判斷同時(shí)計(jì)算需要播放的時(shí)間
        if (!duration) {
            duration = (1.0f / 10.0f) * count;
        }
        //把靜態(tài)的圖片轉(zhuǎn)換為動(dòng)態(tài)的image,所以會(huì)有大量單張的圖片存放在內(nèi)存中
        animatedImage = [UIImage animatedImageWithImages:images duration:duration];
    }
    //釋放圖像數(shù)據(jù)讀取任務(wù)的抽象對(duì)象
    CFRelease(source);
    //返回動(dòng)態(tài)圖片
    return animatedImage;
}

解決方案

  • gif播放其實(shí)就是一張一張圖片返回去,我們要寫一個(gè)方法,只要不斷地取出當(dāng)前的那一張圖片,這樣就可以有效的避免內(nèi)存中存儲(chǔ)了大量圖片.那如何實(shí)現(xiàn)不斷地去取呢,我們可以開一個(gè)定時(shí)器,定時(shí)器不斷的去掉我們寫的方法,不斷地去取圖片賦值給imageView.

代碼重寫

  • 我采用創(chuàng)建一個(gè)UIImageView子類來封裝定時(shí)器方法.
  • 需要注意導(dǎo)入相關(guān)的頭文件<ImageIO/ImageIO.h>.
  • 這個(gè)方法的本質(zhì)就是用一個(gè)定時(shí)器不斷的去獲取一張圖片給imageView,這樣就避免了大量圖片存入內(nèi)存中.
  • 附上github源碼github源碼,懶得下的人,源碼就在下面.我也寫了很詳細(xì)的注釋.
  • 實(shí)例代碼中,在一個(gè)界面上用了6個(gè)gif,如果用原生的方法,內(nèi)存會(huì)達(dá)到130M左右,如果采用計(jì)時(shí)器方法,則只有30M左右
#import "WBWebImage.h"
#import <SDWebImageManager.h>
#import <NSData+ImageContentType.h>
#import <UIImage+GIF.h>
#import <ImageIO/ImageIO.h>

@implementation WBWebImage {
    //記錄當(dāng)前是第幾張gif
    NSInteger _currentIndex;
    //定時(shí)器
    NSTimer *_timer;
    //gif圖片的二進(jìn)制數(shù)據(jù)
    NSData *_data;
}
/*
 //1.根據(jù)url去下載圖片的二進(jìn)制數(shù)據(jù)
 //2.根據(jù)圖片的類型判斷如果是gif特殊處理
 //3.如果是其他類型,直接顯示
 */
- (void)WB_downloadIMGOrGif:(NSURL *)url {
    _timer = [NSTimer timerWithTimeInterval:0.12 target:self selector:@selector(updateIMG) userInfo:nil repeats:YES];
    [self downloadIMGData:url];
}
- (void)updateIMG {
    //不斷的調(diào)用生成gif的方法,并且不斷的賦值給imageView
    self.image = [self wb_animatedGIFWithData:_data];
}

//下載圖片
- (void)downloadIMGData:(NSURL *)url {
    //從管理者進(jìn)行查找
    [[SDWebImageManager sharedManager].imageDownloader downloadImageWithURL:url options:0 progress:nil completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) {
        if (error) {
            NSLog(@"下載錯(cuò)誤%@",error);
            return;
        }
        //根據(jù)圖片的類型進(jìn)行判斷;
        //UI操作放在主線程
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            if ([[NSData sd_contentTypeForImageData:data] isEqualToString:@"image/gif"]) {
                //據(jù)圖片的類型判斷如果是gif特殊處理
                _data = data;
                //將定時(shí)器加入到運(yùn)行循環(huán)中
                [[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSDefaultRunLoopMode];
            } else {
                
                self.image = image;
            }
        }];
    }];
}
//原始代碼是把所有的gif全部加載處理完畢才去播放,內(nèi)存占用過多
//修改: 開啟一個(gè)定時(shí)器,不斷的去gif中取出對(duì)應(yīng)的單張圖片
- (UIImage *)wb_animatedGIFWithData:(NSData *)data {
    if (!data) {
        return nil;
    }
    //類型轉(zhuǎn)換
    CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);
    //幾張圖片
    size_t count = CGImageSourceGetCount(source);
    //返回的變量
    UIImage *animatedImage;
    
    if (count <= 1) {
        animatedImage = [[UIImage alloc] initWithData:data];
    }
    else {
        //取出gif中的單張圖片
        CGImageRef image = CGImageSourceCreateImageAtIndex(source, _currentIndex % count, NULL);
        _currentIndex ++;
        //類型的轉(zhuǎn)換
        animatedImage = [UIImage imageWithCGImage:image scale:[UIScreen mainScreen].scale orientation:UIImageOrientationUp];
        
            CGImageRelease(image);
    }
    CFRelease(source);
    return animatedImage;
}
@end

總結(jié)

  • 利用定時(shí)器方法,會(huì)稍微增加一些cpu的負(fù)荷,原因是cpu不斷的再計(jì)算.
  • 利用圖片數(shù)組來做gif,雖然cpu輕松了,但是內(nèi)存負(fù)荷大.
  • 兩個(gè)方法,一個(gè)是用性能去換空間,一個(gè)是用空間換性能.
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,789評(píng)論 25 709
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,074評(píng)論 4 61
  • ?第7篇文章 念書的時(shí)候有一位朋友告訴我 人生就是不斷地遇見困難和挫折 然后不斷將它們解決 如此反復(fù)的過程 今年應(yīng)...
    學(xué)灰小女神閱讀 181評(píng)論 0 0
  • 正文:生命的覺醒是最內(nèi)核的,我們所做的一切都是為它服務(wù)的,無論我們做企業(yè)、做事業(yè)還是做人,一切的一切都是工具,都是...
    李向姿閱讀 572評(píng)論 0 0
  • 我國(guó)有13億人口,9.1億生活在農(nóng)村,農(nóng)村現(xiàn)有勞動(dòng)力4.8億,其中好幾千萬在拍鄉(xiāng)村愛情故事,還有去唱《咱屯里人》了...
    d59c7e9aeadd閱讀 447評(píng)論 0 0

友情鏈接更多精彩內(nèi)容