問題:項目中我們經(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的過程,遇到哨兵對象即停止。