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

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

    //寫入時的邏輯:將數(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è)置每個buffer出現(xiàn)的時間,每個buffer的出現(xiàn)時間為第n張除以60(20是一秒20張圖片,幀率,也可以自己設(shè)置其他值)所以為frame/60,即CMTimeMake(frame,60)為每一個buffer出現(xiàn)的時間點
                CLog(@"frame = %d", frame);
                if(frame >=0&&![adaptor appendPixelBuffer:buffer withPresentationTime:CMTimeMake(frame, DEVideoFrame)]){//設(shè)置每秒鐘播放圖片的個數(shù)
                    // NSLog(@"FAIL");
                }else{
                    // NSLog(@"OK");
                }
                CFRelease(buffer);
            }

分析下原因:
采用ARC內(nèi)存管理,frameImage對象占用內(nèi)存沒有及時釋放,循環(huán)創(chuàng)建,飆升過高造成崩潰。
解決方案:
雖然我們不能直接對frameImage進(jìn)行release操作,但我們可以引入自動釋放池(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后,自動釋放了臨時創(chuàng)建的對象內(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是一個結(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是一個對Page進(jìn)行分頁管理的雙向鏈表。分析push和pop方法(引文在下方)得出,每一個autoreleasepool對象只有一個哨兵,哨兵放在第一頁中;每一頁的大小為4096字節(jié);每一頁的前56個字節(jié)存儲頁的AutoreleasePoolPageData結(jié)構(gòu)體數(shù)據(jù);第一頁的第56往后8個字節(jié)存儲哨兵,后面存儲autorelease對象,總共可以存儲504個;從第二頁開始,每頁可以存儲505個對象;objc_autoreleasepoolpush是一個查找child,遞增next,創(chuàng)建新頁的過程;objc_autoreleasepoolpop是一個查找parent,遞減next,釋放對象,銷毀page的過程,遇到哨兵對象即停止。

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

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