美文网首页收藏iosiOS plus基础应用
iOS原理 理解AutoreleasePool释放对象的时机

iOS原理 理解AutoreleasePool释放对象的时机

作者: 东篱采桑人 | 来源:发表于2021-01-05 16:48 被阅读0次

    iOS原理 文章汇总

    在介绍AutoreleasePool基本概念的时候提到,AutoreleasePool在销毁时,会统一给池中的所有对象发送一次release消息。通过这个机制,我们可以立即或者延迟释放对象。

    延迟释放

    Runloop创建的AutoreleasePool,只会在Runloop即将休眠或退出的时候销毁,这时候池中的对象才会被释放,因此可以达到延迟释放的效果。这里以主线程Runloop为例,看看主线程中的对象被延迟释放的情况。

    • 断点查看主线程中的AutoreleasePool

    如图所示,直接在viewDidLoad方法里下断点,运行后在控制台进行bt输出,结果如下:

    在输出结果里可以看到,此时主线程Runloop已经创建了一个AutoreleasePool

    • 在主线程中创建自动释放对象

    这里创建两个UIImage对象,其中一个是自动释放对象,另一个为非自动释放对象,以作对比。

    //弱引用指针不会持有对象,不影响对象的引用计数
    __weak UIImage *_img1 = nil;
    __weak UIImage *_img2 = nil;
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view.
    
        //创建两个UIImage对象
        UIImage *img1 = [[UIImage alloc] init];            //非自动释放对象,不能被添加到AutoreleasePool
        UIImage *img2 = [UIImage imageNamed:@"test.png"];  //自动释放对象,会被添加到AutoreleasePool
        _img1 = img1;
        _img2 = img2;
        //打印两个对象的地址
        NSLog(@" ==== %@:img1 = %p, img2 = %p", NSStringFromSelector(_cmd), _img1, _img2);
    }
    
    -(void)viewWillAppear:(BOOL)animated{
        
        [super viewWillAppear:animated];
        //打印两个对象的地址
        NSLog(@" ==== %@:img1 = %p, img2 = %p", NSStringFromSelector(_cmd), _img1, _img2);
    }
    
    -(void)viewDidAppear:(BOOL)animated{
        
        [super viewDidAppear:animated];
        //打印两个对象的地址
        NSLog(@" ==== %@:img1 = %p, img2 = %p", NSStringFromSelector(_cmd), _img1, _img2);
    }
    
    //打印结果
    ==== viewDidLoad:img1 = 0x600003274990, img2 = 0x6000032705a0
    ==== viewWillAppear::img1 = 0x0, img2 = 0x6000032705a0
    ==== viewDidAppear::img1 = 0x0, img2 = 0x0
    

    从打印结果可知,_img1viewDidLoad方法结束时就被释放了,而_img2被延迟释放了。这是因为:

    • _img1:是在viewDidLoad方法里创建的临时对象,且为非自动释放对象,所有权归viewDidLoad方法持有,当方法结束时,就会被释放掉。

    • _img2:虽然也是在viewDidLoad方法里创建的临时对象,但它是自动释放对象,会被添加到主线程Runloop创建的AutoreleasePool中,所有权归AutoreleasePool持有,只有当Runloop即将进入休眠或者退出时,才会销毁AutoreleasePool,这时对象才能被释放,所以会延迟释放。

    因此,程序运行后,主线程Runloop会自动创建AutoreleasePool,在主线程中创建的自动释放对象都会被添加到AutoreleasePool中,这些对象都会被延迟释放。

    立即释放

    使用@autoreleasepool {}代码块手动创建的AutoreleasePool,当超出代码块的作用域时被销毁,这时会释放池中的对象,可以达到立即释放的效果。一般情况下,在编写循环时可能需要手动创建AutoreleasePool

    • 循环内创建AutoreleasePool
    for (int i = 0; i<1000000; i++) {
    
        @autoreleasepool {
    
            UIImage *img = [UIImage imageNamed:@"test.png"];
            NSLog(@" ==== %p", img);
        }
    }
    

    运行后可以看到,在循环过程中内存并不会上涨。这是因为
    每次迭代都会创建并销毁一个AutoreleasePool,每次创建的UIImage对象都会被添加到AutoreleasePool,在销毁时就被释放了,所以内存不会上涨。

    因此,在循环内使用自动释放池块可以在下一次迭代之前释放这些临时对象,这样就可以降低内存峰值。需要注意的是,如果循环内都是非自动释放对象,就不需要手动创建AutoreleasePool,即使创建了也起不了任何作用。

    基于降低内存峰值的需求,苹果自己也在下面这几个方法里封装了@autoreleasepool {},遍历时使用这几个方法性能更好。

    • enumerateObjectsUsingBlock
    • enumerateObjectsWithOptions
    • enumerateObjectsAtIndexes

    推荐阅读

    iOS原理 AutoreleasePool的基本概念

    相关文章

      网友评论

        本文标题:iOS原理 理解AutoreleasePool释放对象的时机

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