問題:項(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ì)象即停止。