优化iOS的内存-自动释放池

问题:项目中我们经常遇到一种情况,因为某个功能需要创建很多临时变量,而且这些变量比较耗内存,还会造成崩溃。比如下面是渲染视频每一帧的代码,很耗内存,而且上限不可控,乃至崩溃:

    //写入时的逻辑:将数组中的每一张图片多次写入到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);
            // 将图片转成buffer
            buffer = (CVPixelBufferRef)[self pixelBufferFromCGImage:subImageRef withBottomCGImage:bottomCGImage];
            CGImageRelease(subImageRef);
            if(buffer){
                //添加buffer并设置每个buffer出现的时间,每个buffer的出现时间为第n张除以60(20是一秒20张图片,帧率,也可以自己设置其他值)所以为frame/60,即CMTimeMake(frame,60)为每一个buffer出现的时间点
                CLog(@"frame = %d", frame);
                if(frame >=0&&![adaptor appendPixelBuffer:buffer withPresentationTime:CMTimeMake(frame, DEVideoFrame)]){//设置每秒钟播放图片的个数
                    // NSLog(@"FAIL");
                }else{
                    // NSLog(@"OK");
                }
                CFRelease(buffer);
            }

分析下原因:
采用ARC内存管理,frameImage对象占用内存没有及时释放,循环创建,飙升过高造成崩溃。
解决方案:
虽然我们不能直接对frameImage进行release操作,但我们可以引入自动释放池(AutoreleasePool),

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

循环内引入,

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

增加@autoreleasepool后,自动释放了临时创建的对象内存,内存没有没有明显的上涨。
拓展:
看下入口main函数,

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

源码如图:


main函数

@autoreleasepool的底层代码__AtAutoreleasePool __autoreleasepool__AtAutoreleasePool是一个结构体。

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

__AtAutoreleasePool __autoreleasepool;相当于执行了__AtAutoreleasePool的构造函数和析构函数

atautoreleasepoolobj = objc_autoreleasePoolPush();
objc_autoreleasePoolPop(atautoreleasepoolobj);

push是Page执行,pop同理

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

Page继承自PageData

Page

PageData如下:
PageData

结论:分析PageData结构体,看出autoreleasepool是一个对Page进行分页管理的双向链表。分析push和pop方法(引文在下方)得出,每一个autoreleasepool对象只有一个哨兵,哨兵放在第一页中;每一页的大小为4096字节;每一页的前56个字节存储页的AutoreleasePoolPageData结构体数据;第一页的第56往后8个字节存储哨兵,后面存储autorelease对象,总共可以存储504个;从第二页开始,每页可以存储505个对象;objc_autoreleasepoolpush是一个查找child,递增next,创建新页的过程;objc_autoreleasepoolpop是一个查找parent,递减next,释放对象,销毁page的过程,遇到哨兵对象即停止。

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容