優(yōu)化iOS的內(nèi)存-自動(dòng)釋放池

問題:項(xiàng)目中我們經(jīng)常遇到一種情況,因?yàn)槟硞€(gè)功能需要?jiǎng)?chuàng)建很多臨時(shí)變量,而且這些變量比較耗內(nèi)存,還會(huì)造成崩潰。比如下面是渲染視頻每一幀的代碼,很耗內(nèi)存,而且上限不可控,乃至崩潰:

    //寫入時(shí)的邏輯:將數(shù)組中的每一張圖片多次寫入到buffer中,
    while([writerInput isReadyForMoreMediaData]){
        CVPixelBufferRef buffer =NULL;
        if (frame < frameCount) {
            UIImage *frameImage = [export getImageWithCurrentFrame:frame];
            CGImageRef imageRef = frameImage.CGImage;
            // 裁剪
            CGImageRef subImageRef = CGImageCreateWithImageInRect(imageRef, self.svgaCropRect);
            // 將圖片轉(zhuǎn)成buffer
            buffer = (CVPixelBufferRef)[self pixelBufferFromCGImage:subImageRef withBottomCGImage:bottomCGImage];
            CGImageRelease(subImageRef);
            if(buffer){
                //添加buffer并設(shè)置每個(gè)buffer出現(xiàn)的時(shí)間,每個(gè)buffer的出現(xiàn)時(shí)間為第n張除以60(20是一秒20張圖片,幀率,也可以自己設(shè)置其他值)所以為frame/60,即CMTimeMake(frame,60)為每一個(gè)buffer出現(xiàn)的時(shí)間點(diǎn)
                CLog(@"frame = %d", frame);
                if(frame >=0&&![adaptor appendPixelBuffer:buffer withPresentationTime:CMTimeMake(frame, DEVideoFrame)]){//設(shè)置每秒鐘播放圖片的個(gè)數(shù)
                    // NSLog(@"FAIL");
                }else{
                    // NSLog(@"OK");
                }
                CFRelease(buffer);
            }

分析下原因:
采用ARC內(nèi)存管理,frameImage對(duì)象占用內(nèi)存沒有及時(shí)釋放,循環(huán)創(chuàng)建,飆升過高造成崩潰。
解決方案:
雖然我們不能直接對(duì)frameImage進(jìn)行release操作,但我們可以引入自動(dòng)釋放池(AutoreleasePool),

@autoreleasepool {
 // Code benefitting from a local autorelease pool.
}

循環(huán)內(nèi)引入,

            @autoreleasepool {
                UIImage *frameImage = [export getImageWithCurrentFrame:frame];
                CGImageRef imageRef = frameImage.CGImage;
                // 裁剪
                CGImageRef subImageRef = CGImageCreateWithImageInRect(imageRef, self.svgaCropRect);
                // 將圖片轉(zhuǎn)成buffer
                buffer = (CVPixelBufferRef)[self pixelBufferFromCGImage:subImageRef withBottomCGImage:bottomCGImage];
                CGImageRelease(subImageRef);

增加@autoreleasepool后,自動(dòng)釋放了臨時(shí)創(chuàng)建的對(duì)象內(nèi)存,內(nèi)存沒有沒有明顯的上漲。
拓展:
看下入口main函數(shù),

int main(int argc, char * argv[]) {
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

源碼如圖:


main函數(shù)

@autoreleasepool的底層代碼__AtAutoreleasePool __autoreleasepool__AtAutoreleasePool是一個(gè)結(jié)構(gòu)體。

struct __AtAutoreleasePool {
  __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
  ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
  void * atautoreleasepoolobj;
};

__AtAutoreleasePool __autoreleasepool;相當(dāng)于執(zhí)行了__AtAutoreleasePool的構(gòu)造函數(shù)和析構(gòu)函數(shù)

atautoreleasepoolobj = objc_autoreleasePoolPush();
objc_autoreleasePoolPop(atautoreleasepoolobj);

push是Page執(zhí)行,pop同理

void *
objc_autoreleasePoolPush(void)
{
    return AutoreleasePoolPage::push();
}

Page繼承自PageData

Page

PageData如下:
PageData

結(jié)論:分析PageData結(jié)構(gòu)體,看出autoreleasepool是一個(gè)對(duì)Page進(jìn)行分頁管理的雙向鏈表。分析push和pop方法(引文在下方)得出,每一個(gè)autoreleasepool對(duì)象只有一個(gè)哨兵,哨兵放在第一頁中;每一頁的大小為4096字節(jié);每一頁的前56個(gè)字節(jié)存儲(chǔ)頁的AutoreleasePoolPageData結(jié)構(gòu)體數(shù)據(jù);第一頁的第56往后8個(gè)字節(jié)存儲(chǔ)哨兵,后面存儲(chǔ)autorelease對(duì)象,總共可以存儲(chǔ)504個(gè);從第二頁開始,每頁可以存儲(chǔ)505個(gè)對(duì)象;objc_autoreleasepoolpush是一個(gè)查找child,遞增next,創(chuàng)建新頁的過程;objc_autoreleasepoolpop是一個(gè)查找parent,遞減next,釋放對(duì)象,銷毀page的過程,遇到哨兵對(duì)象即停止。

引文:https://zhuanlan.zhihu.com/p/321687906?utm_id=0

最后編輯于
?著作權(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)容

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