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

內(nèi)存過(guò)高

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

原因分析

  • 先看SDWebImage的源代碼,SDWebImage通過(guò)這個(gè)類(lèi)UIImage+GIF.h來(lái)處理gif,我們進(jìn)入頭文件發(fā)現(xiàn)會(huì)調(diào)用一個(gè)+ (UIImage *)sd_animatedGIFWithData:(NSData *)data 這樣的類(lèi)方法
    下面是這個(gè)方法的源代碼,我已經(jīng)加了很詳細(xì)的注釋,并且把問(wèn)題的所在也寫(xiě)的很清楚.
+ (UIImage *)sd_animatedGIFWithData:(NSData *)data {
    //安全判斷
    if (!data) {
        return nil;
    }
    //二進(jìn)制類(lèi)型的轉(zhuǎn)換
    //CGImageSourceRef是個(gè)什么呢? 我們可以看到這是一個(gè)typedef CGImageSource * CGImageSourceRef;
    //這是一個(gè)指針,CGImageSource是對(duì)圖像數(shù)據(jù)讀取任務(wù)的抽象扶供,通過(guò)它可以獲得圖像對(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];
    }
    //如果為多張圖片,就開(kāi)始創(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í)就是一張一張圖片返回去,我們要寫(xiě)一個(gè)方法,只要不斷地取出當(dāng)前的那一張圖片,這樣就可以有效的避免內(nèi)存中存儲(chǔ)了大量圖片.那如何實(shí)現(xiàn)不斷地去取呢,我們可以開(kāi)一個(gè)定時(shí)器,定時(shí)器不斷的去掉我們寫(xiě)的方法,不斷地去取圖片賦值給imageView.

代碼重寫(xiě)

  • 我采用創(chuàng)建一個(gè)UIImageView子類(lèi)來(lái)封裝定時(shí)器方法.
  • 需要注意導(dǎo)入相關(guān)的頭文件<ImageIO/ImageIO.h>.
  • 這個(gè)方法的本質(zhì)就是用一個(gè)定時(shí)器不斷的去獲取一張圖片給imageView,這樣就避免了大量圖片存入內(nèi)存中.
  • 附上github源碼github源碼,懶得下的人,源碼就在下面.我也寫(xiě)了很詳細(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ù)圖片的類(lèi)型判斷如果是gif特殊處理
 //3.如果是其他類(lèi)型,直接顯示
 */
- (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ù)圖片的類(lèi)型進(jìn)行判斷;
        //UI操作放在主線(xiàn)程
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            if ([[NSData sd_contentTypeForImageData:data] isEqualToString:@"image/gif"]) {
                //據(jù)圖片的類(lèi)型判斷如果是gif特殊處理
                _data = data;
                //將定時(shí)器加入到運(yùn)行循環(huán)中
                [[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSDefaultRunLoopMode];
            } else {
                
                self.image = image;
            }
        }];
    }];
}
//原始代碼是把所有的gif全部加載處理完畢才去播放,內(nèi)存占用過(guò)多
//修改: 開(kāi)啟一個(gè)定時(shí)器,不斷的去gif中取出對(duì)應(yīng)的單張圖片
- (UIImage *)wb_animatedGIFWithData:(NSData *)data {
    if (!data) {
        return nil;
    }
    //類(lèi)型轉(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 ++;
        //類(lèi)型的轉(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ù)組來(lái)做gif,雖然cpu輕松了,但是內(nèi)存負(fù)荷大.
  • 兩個(gè)方法,一個(gè)是用性能去換空間,一個(gè)是用空間換性能.
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市掌桩,隨后出現(xiàn)的幾起案子边锁,更是在濱河造成了極大的恐慌,老刑警劉巖拘鞋,帶你破解...
    沈念sama閱讀 219,270評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件砚蓬,死亡現(xiàn)場(chǎng)離奇詭異矢门,居然都是意外死亡盆色,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)祟剔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)隔躲,“玉大人,你說(shuō)我怎么就攤上這事物延⌒担” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,630評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵叛薯,是天一觀(guān)的道長(zhǎng)浑吟。 經(jīng)常有香客問(wèn)我,道長(zhǎng)耗溜,這世上最難降的妖魔是什么组力? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,906評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮抖拴,結(jié)果婚禮上燎字,老公的妹妹穿的比我還像新娘。我一直安慰自己阿宅,他們只是感情好候衍,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著洒放,像睡著了一般蛉鹿。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上往湿,一...
    開(kāi)封第一講書(shū)人閱讀 51,718評(píng)論 1 305
  • 那天妖异,我揣著相機(jī)與錄音,去河邊找鬼煌茴。 笑死随闺,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蔓腐。 我是一名探鬼主播矩乐,決...
    沈念sama閱讀 40,442評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了散罕?” 一聲冷哼從身側(cè)響起分歇,我...
    開(kāi)封第一講書(shū)人閱讀 39,345評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎欧漱,沒(méi)想到半個(gè)月后职抡,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,802評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡误甚,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評(píng)論 3 337
  • 正文 我和宋清朗相戀三年缚甩,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片窑邦。...
    茶點(diǎn)故事閱讀 40,117評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡擅威,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出冈钦,到底是詐尸還是另有隱情郊丛,我是刑警寧澤,帶...
    沈念sama閱讀 35,810評(píng)論 5 346
  • 正文 年R本政府宣布瞧筛,位于F島的核電站厉熟,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏较幌。R本人自食惡果不足惜揍瑟,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望绅络。 院中可真熱鬧月培,春花似錦、人聲如沸恩急。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,011評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)衷恭。三九已至此叠,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間随珠,已是汗流浹背灭袁。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,139評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留窗看,地道東北人茸歧。 一個(gè)月前我還...
    沈念sama閱讀 48,377評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像显沈,于是被迫代替她去往敵國(guó)和親软瞎。 傳聞我的和親對(duì)象是個(gè)殘疾皇子逢唤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

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