美文网首页
优化iOS的内存-自动释放池

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

作者: 恩莱客 | 来源:发表于2024-01-16 18:15 被阅读0次

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

        //写入时的逻辑:将数组中的每一张图片多次写入到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

    相关文章

      网友评论

          本文标题:优化iOS的内存-自动释放池

          本文链接:https://www.haomeiwen.com/subject/cbwkodtx.html