iOS 內(nèi)存監(jiān)控與治理(四)- foom問題治理與優(yōu)化

本章節(jié)列舉對內(nèi)存使用不合理的場景及優(yōu)化方案,在實(shí)際的研發(fā)中還是需要同學(xué)們嚴(yán)格遵守代碼規(guī)范,避免踩坑。

共有以下幾點(diǎn):
1、使用NSCache
2、避免內(nèi)存泄漏
3、移除當(dāng)前未使用的內(nèi)存
4、優(yōu)化圖片使用

使用緩存

使用 NSCache不僅能保證線程安全,同時在收到內(nèi)存警告時,可以釋放未使用的內(nèi)存,此外針對compressed memory,NSCache 也有優(yōu)化。
在實(shí)際的研發(fā)中,也推薦對緩存設(shè)置上限,比如YY 中關(guān)于圖片緩存設(shè)置一個最大的緩存值。對于緩存設(shè)置上限后引入的緩存清除策略,也可以參考YY 使用 LRU(least recently use)方式,更新緩存值。

避免內(nèi)存泄漏

內(nèi)存泄漏是指申請的內(nèi)存空間使用完畢之后未回收。造成內(nèi)存泄漏的原因有很多,各個場景也不盡相同,下面列舉常見一些場景。

Block 使用不當(dāng)?shù)仍蛟斐傻难h(huán)引用
  • self 持有的Block 內(nèi)直接使用了self,這種屬于非常常見的循環(huán)引用了,不過這里還是不推薦大家遇到Block無腦使用weakSelf,使用 weakSelf是會造成額外的性能損耗的,像常見的使用GCD Block內(nèi)是不需要使用弱引用的
  • 隱含的循環(huán)引用,比如superView -> self.xxxView -> self.xxxView.block -> superView。有一些場景并不會直接使用self關(guān)鍵字,但是由于addSubview等方式會持有一些子View,這些子View 又可能持有其他子View且這些子View 可能會使用Block,這時候就需要特別注意,最好理清所有對象的引用關(guān)系,避免循環(huán)引用。
子線程runloop沒有停掉,導(dǎo)致子線程一直存活。

一般來說子線程在任務(wù)結(jié)束后 會自動關(guān)閉,但是如果子線程中由于定時器功能,會打開runloop,如果不先停止runloop的話,子線程也就無法正常關(guān)閉

移除未使用的內(nèi)存

  • 消息列表中已不可見的消息數(shù)據(jù)
  • 進(jìn)入后臺后,當(dāng)前頁面可以移除

優(yōu)化圖片使用

圖片所占用的內(nèi)存對于大多數(shù)APP 來說都是不能忽視的,合理的使用圖片不僅需要研發(fā)盡量保證編碼規(guī)范,還需要實(shí)施一些圖片大小監(jiān)控。

1、避免將圖片放在內(nèi)存里
  • 解碼后的UIImage 占用的內(nèi)存比較大,如果當(dāng)前不需要顯示時,可以不放在內(nèi)存里
  • 同一個頁面使用重復(fù)資源時,避免重復(fù)創(chuàng)建(比如 解碼的YYImage)
  • 批量使用圖片的場景,推薦使用autoreleasepool等方式及時釋放內(nèi)存
  • 大圖不放在Asset中且通過 imageWithContentOfFile讀取
2、圖片裁剪

大多情況下業(yè)務(wù)場景需要顯示的圖片尺寸小于圖片的原始尺寸。圖片裁剪一般有兩種選擇,一是在后端下發(fā)圖片的時候,不同場景下發(fā)不同尺寸的URL;二是圖片下載后,客戶端對原圖進(jìn)行合適的裁剪。

3、圖片繪制及縮放使用 UIGraphicsImageRenderer 和 ImageIO

常見的UIGraphicsBeginImageContextWithOptions繪圖方式會有兩個問題:
(1)默認(rèn)是 SRGB 的格式,也就是說每個像素需要占 4 個 bytes 的空間,對于一些黑白或者僅有 alpha 通道的數(shù)據(jù)來說是沒有必要的。
(2)需要將原圖片完全解碼后渲染出來,原圖片的解碼會造成內(nèi)存占用的高峰。

iOS 12之后,使用UIGraphicsImageRenderer繪圖。系統(tǒng)會自動選擇合適的顏色格式,避免不必要的內(nèi)存消耗。
對于原方法需要解碼原圖造成內(nèi)存暴漲的問題,可以考慮用ImageIO來解決。ImageIO可以直接讀取圖像大小和元數(shù)據(jù)信息,不會帶來額外的內(nèi)存開銷。

這里附上ImageIO 創(chuàng)建縮略圖的使用代碼

+ (UIImage *)getThumbImageWithMax:(CGFloat)max imageData:(NSData *)imageData {
    if (imageData.length == 0) {
        return nil;
    }
    CFDictionaryRef dicOptionsRef = (__bridge CFDictionaryRef) @{(id)kCGImageSourceCreateThumbnailFromImageAlways : @(YES),
                                                                 (id)kCGImageSourceThumbnailMaxPixelSize : @(max),
                                                                 (id)kCGImageSourceShouldCache : @(NO),
                                                                 (id)kCGImageSourceCreateThumbnailWithTransform:@(YES)
    };
    
    CGImageSourceRef src = CGImageSourceCreateWithData((__bridge CFDataRef)imageData, nil);
    CGImageRef imageRef = CGImageSourceCreateThumbnailAtIndex(src, 0, dicOptionsRef);
    UIImage *newImage = [UIImage imageWithCGImage:imageRef];
    if (imageRef != nil) CFRelease(imageRef);
    CFRelease(src);
    return newImage;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

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