圖片加載過程及代碼優(yōu)化方案

圖片的加載流程

參考下圖大致的加載流程


圖片操作流程.png

圖片初始創(chuàng)建是不會解壓的伶跷,只有在顯示前才會去準備解壓筋蓖,這樣如果有很多圖片同時需要展示就會造成主線任務(wù)繁重侣诺。

另外在我們展示圖片是有時為了降低頻繁操作工作量,會選擇異步解碼圖片弛姜。主體思路為:在子線程糜芳,將原始的圖片渲染成新的以字節(jié)顯示的圖片飒货。

代碼操作如下(SDWebImage解決方案),其本質(zhì)是:

在子線程直接通過新創(chuàng)建一張位圖的方式繪制一張圖片峭竣。此時就已經(jīng)完成了解壓操作塘辅,可以后續(xù)直接使用。

// 以下代碼需要自己操作異步方案處理
+ (UIImage *)decodedImageWithImage:(UIImage *)image {
    if (image.images) {
        // Do not decode animated images
        return image;
    }
    
    CGImageRef imageRef = image.CGImage;
    CGSize imageSize = CGSizeMake(CGImageGetWidth(imageRef), CGImageGetHeight(imageRef));
    CGRect imageRect = (CGRect){.origin = CGPointZero, .size = imageSize};
    
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef);
    
    int infoMask = (bitmapInfo & kCGBitmapAlphaInfoMask);
    BOOL anyNonAlpha = (infoMask == kCGImageAlphaNone ||
                        infoMask == kCGImageAlphaNoneSkipFirst ||
                        infoMask == kCGImageAlphaNoneSkipLast);
    
    // CGBitmapContextCreate doesn't support kCGImageAlphaNone with RGB.
    // https://developer.apple.com/library/mac/#qa/qa1037/_index.html
    if (infoMask == kCGImageAlphaNone && CGColorSpaceGetNumberOfComponents(colorSpace) > 1) {
        // Unset the old alpha info.
        bitmapInfo &= ~kCGBitmapAlphaInfoMask;
        
        // Set noneSkipFirst.
        bitmapInfo |= kCGImageAlphaNoneSkipFirst;
    }
    // Some PNGs tell us they have alpha but only 3 components. Odd.
    else if (!anyNonAlpha && CGColorSpaceGetNumberOfComponents(colorSpace) == 3) {
        // Unset the old alpha info.
        bitmapInfo &= ~kCGBitmapAlphaInfoMask;
        bitmapInfo |= kCGImageAlphaPremultipliedFirst;
    }
    
    // It calculates the bytes-per-row based on the bitsPerComponent and width arguments.
    CGContextRef context = CGBitmapContextCreate(NULL,
                                                 imageSize.width,
                                                 imageSize.height,
                                                 CGImageGetBitsPerComponent(imageRef),
                                                 0,
                                                 colorSpace,
                                                 bitmapInfo);
    CGColorSpaceRelease(colorSpace);
    
    // If failed, return undecompressed image
    if (!context) return image;
    
    CGContextDrawImage(context, imageRect, imageRef);
    CGImageRef decompressedImageRef = CGBitmapContextCreateImage(context);
    
    CGContextRelease(context);
    
    UIImage *decompressedImage = [UIImage imageWithCGImage:decompressedImageRef scale:image.scale orientation:image.imageOrientation];
    CGImageRelease(decompressedImageRef);
    return decompressedImage;
}


繪制一個純色的圖片

  • 使用UIGraphicsImageRenderer進行繪制皆撩,內(nèi)部會走CoreImage這一套

    推薦使用這種方式扣墩,擁有緩存機制使用更加方便,不用管理上下文毅访。iOS10后可用

    UIGraphicsImageRenderer *render = [[UIGraphicsImageRenderer alloc] initWithSize:CGSizeMake(100, 100)];  // 指定渲染區(qū)域大小
    UIImage *img = [render imageWithActions:^(UIGraphicsImageRendererContext * _Nonnull rendererContext) {
        [UIColor.blueColor setFill];
        [rendererContext fillRect:CGRectMake(0, 0, 100, 100)];
    }];
  • 使用Quartz2D繪制
    UIGraphicsBeginImageContextWithOptions(CGSizeMake(100, 100), NO, UIScreen.mainScreen.scale);
    [UIColor.redColor setFill];
    CGContextFillRect(UIGraphicsGetCurrentContext(),CGRectMake(0, 0, 100, 100));
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    
    UIImageView *imgV = [[UIImageView alloc] initWithFrame:CGRectMake(10, 3, 100, 100)];
    imgV.image = image;
    [self.view addSubview:imgV];

壓縮大圖至指定尺寸

  • 使用以下方案沮榜,會有內(nèi)存峰值出現(xiàn)

- (void)testDrawWithRender {
    
    UIImage *image = [UIImage imageNamed:@"zz"];
    
    UIGraphicsImageRenderer *render = [[UIGraphicsImageRenderer alloc] initWithSize:self.renderSize];
    UIImage *img = [render imageWithActions:^(UIGraphicsImageRendererContext * _Nonnull rendererContext) {
        [image drawInRect:CGRectMake(0, 0, self.renderSize.width, self.renderSize.height)];
    }];
    
    UIImageView *imgV = [[UIImageView alloc] initWithFrame:CGRectMake(10, 3, self.renderSize.width, self.renderSize.height)];
    imgV.image = img;
    [self.view addSubview:imgV];
}

  • 使用ImageIO渲染出一張縮略圖

    ImageIO能夠在不產(chǎn)生dirty memory的情況下讀取到圖片尺寸和元數(shù)據(jù)信息,其內(nèi)存損耗等于縮減后的圖片尺寸產(chǎn)生的內(nèi)存占用喻粹。


- (void)testDrawWithImageIO {
    
    NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"zz" ofType:@"png"]];
    CGImageSourceRef imageSourceRef = CGImageSourceCreateWithURL((__bridge CFURLRef)url, NULL);
    
//  UIImage *image = [UIImage imageNamed:@"zz.png"];        // 如果使用了這種方式蟆融,那么大圖一定會加載到內(nèi)存中,會有峰值出現(xiàn)
//  NSData *data = UIImagePNGRepresentation(image);
//  CGImageSourceRef imageSourceRef = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);
    
    CFDictionaryRef options = (__bridge CFDictionaryRef)@{
        (id)kCGImageSourceCreateThumbnailFromImageIfAbsent:@(YES),
        (id)kCGImageSourceThumbnailMaxPixelSize:@100,   // 最大像素守呜,如果設(shè)置很小就不清楚
        (id)kCGImageSourceShouldCache:@YES
    };
    
    // 生成縮略圖
    CGImageRef imageRef = CGImageSourceCreateThumbnailAtIndex(imageSourceRef, 0, options);
    UIImage *img = [UIImage imageWithCGImage:imageRef];
    
    // 釋放內(nèi)存
    CGImageRelease(imageRef);
    CFRelease(imageSourceRef);
    
    UIImageView *imgV = [[UIImageView alloc] initWithFrame:CGRectMake(10, 3, self.renderSize.width, self.renderSize.height)];
    imgV.image = img;
    [self.view addSubview:imgV];
    
}

常用枚舉屬性解析析:

  • kCGImageSourceThumbnailMaxPixelSize

    設(shè)置縮略圖最大像素值型酥,圖片尺寸不會發(fā)生改變

  • kCGImageSourceCreateThumbnailFromImageIfAbsent

    如果圖像源文件中不存在縮略圖,是否應(yīng)自動為圖像創(chuàng)建縮略圖查乒。

    縮略圖是根據(jù)完整圖像創(chuàng)建的弥喉,受kCGImageSourceThumbnailMaxPixelSize指定的限制。

    如果未指定最大像素大小玛迄,則縮略圖是完整圖像的大小由境,這在大多數(shù)情況下是不可取的。此鍵必須是CFBoolean值。默認值為kCFBooleanFalse虏杰。

    可以在傳遞給函數(shù)CGImageSourceCreateThumbnailAtIndex的選項字典中提供此鍵讥蟆。

  • kCGImageSourceCreateThumbnailFromImageAlways

    是否應(yīng)根據(jù)完整圖像創(chuàng)建縮略圖,即使圖像源文件中存在縮略圖纺阔。

  • kCGImageSourceCreateThumbnailWithTransform

    縮略圖是否應(yīng)根據(jù)全圖像的方向和像素縱橫比進行旋轉(zhuǎn)和縮放瘸彤。此鍵的值必須是CFBoolean值。默認值為kCFBooleanFalse笛钝。

  • kCGImageSourceShouldCache

    圖像是否應(yīng)以解碼形式緩存


總結(jié)

通過了解圖片加載機制质况,可以對后續(xù)優(yōu)化方向和理解一些框架設(shè)計思路有很大的幫助。
本文記錄些代碼操作都是工作中常用的方案(復制可用)玻靡,在此也作為一個留檔结榄。


參考:

https://segmentfault.com/a/1190000002776279

https://blog.jamchenjun.com/2018/08/22/image-and-graphics-best-practices.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市啃奴,隨后出現(xiàn)的幾起案子潭陪,更是在濱河造成了極大的恐慌雄妥,老刑警劉巖最蕾,帶你破解...
    沈念sama閱讀 216,744評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異老厌,居然都是意外死亡瘟则,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評論 3 392
  • 文/潘曉璐 我一進店門枝秤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來醋拧,“玉大人,你說我怎么就攤上這事淀弹〉ず荆” “怎么了?”我有些...
    開封第一講書人閱讀 163,105評論 0 353
  • 文/不壞的土叔 我叫張陵薇溃,是天一觀的道長菌赖。 經(jīng)常有香客問我,道長沐序,這世上最難降的妖魔是什么琉用? 我笑而不...
    開封第一講書人閱讀 58,242評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮策幼,結(jié)果婚禮上邑时,老公的妹妹穿的比我還像新娘。我一直安慰自己特姐,他們只是感情好晶丘,可當我...
    茶點故事閱讀 67,269評論 6 389
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著唐含,像睡著了一般浅浮。 火紅的嫁衣襯著肌膚如雪滤钱。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,215評論 1 299
  • 那天脑题,我揣著相機與錄音件缸,去河邊找鬼。 笑死叔遂,一個胖子當著我的面吹牛他炊,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播已艰,決...
    沈念sama閱讀 40,096評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼痊末,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了哩掺?” 一聲冷哼從身側(cè)響起凿叠,我...
    開封第一講書人閱讀 38,939評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎嚼吞,沒想到半個月后盒件,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,354評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡舱禽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,573評論 2 333
  • 正文 我和宋清朗相戀三年炒刁,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片誊稚。...
    茶點故事閱讀 39,745評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡翔始,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出里伯,到底是詐尸還是另有隱情城瞎,我是刑警寧澤,帶...
    沈念sama閱讀 35,448評論 5 344
  • 正文 年R本政府宣布疾瓮,位于F島的核電站脖镀,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏爷贫。R本人自食惡果不足惜认然,卻給世界環(huán)境...
    茶點故事閱讀 41,048評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望漫萄。 院中可真熱鬧卷员,春花似錦、人聲如沸腾务。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,683評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至未巫,卻和暖如春窿撬,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背叙凡。 一陣腳步聲響...
    開封第一講書人閱讀 32,838評論 1 269
  • 我被黑心中介騙來泰國打工劈伴, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人握爷。 一個月前我還...
    沈念sama閱讀 47,776評論 2 369
  • 正文 我出身青樓跛璧,卻偏偏與公主長得像,于是被迫代替她去往敵國和親新啼。 傳聞我的和親對象是個殘疾皇子追城,可洞房花燭夜當晚...
    茶點故事閱讀 44,652評論 2 354