• Autorelease Pool


    前言

    之前有写过一篇关于AutoreleasePool的理解的总结,总觉的不够深入,所以重起一篇。本篇从阅读官方文档和autorelasepool的源码入手来记录。

    Autorelease Pool

    什么是autorelease pool

     从官方文档中的解释,autorelease pool存储着一些对象,当向pool发送drain消息时会向池中全部对象发送release消息。

    autorelease pool的创建和池内对象的释放时机

    应用程序在每次事件循环(event loop)的开始,自动在主线程上创建一个自动释放池,并在每次事件循环(event loop)结束时向池内对象发送release消息进行释放。手动创建的autorelease pool中的对象在block外被释放掉。

    autorelease pool 与 thread

    当你通过NSThread另外开辟的子线程时,你需要自己创建一个autorelease pool。子线程默认是没有自动释放池的。但是使用GCD开辟的线程会自动创建pool。

    autorelease pool的结构

    我们都知道在iOS开发中,main函数作为程序的入口,包含了一个@autoreleasepool,我们通过clang重新编译一下会发现main函数的实现变成了下图的样子

    @autoreleasepool被转成了一个__AtAutoreleasePool。__AtAutoreleasePool是一个结构体

    结构体的构造函数中会调用objc_autoreleasePoolPush(),析构函数中会调用objc_autoreleasePoolPop(void *)

    这俩个方法可以再NSObject.mm的源码中找到实现。 

    方法实现中出现了一个新的类AutoreleasePoolPage,提取出来的大致定义是这样的

    magic 用来校验 AutoreleasePoolPage 的结构是否完整;

    next 指向最新添加的 autoreleased 对象的下一个位置,初始化时指向 begin() ;

    thread 指向当前线程;

    parent 指向父结点,第一个结点的 parent 值为 nil ;

    child 指向子结点,最后一个结点的 child 值为 nil ;

    depth 代表深度,从 0 开始,往后递增 1;

    hiwat 代表 high water mark 

    当next指向begin时代表该page为空,当next指向end时代表page已满。

    autorelease pool就是由这些page组成的双向链表结构且每一个page的可用空间是固定的4096个字节

    objc_autoreleasePoolPush

    上面说了_AtAutoreleasePool在初始化的时候会调用objc_autoreleasePoolPush,我们来看下这个方法的实现,整理后差不多是这样

    调用autoreleaseFast并传入POOL_BOUNDARY。

    POOL_BOUNDARY(哨兵)

    POOL_BOUNDARY实际上就是nil,当调用objc_autoreleasePoolPush方法的时候,POOL_BOUNDARY被最先加入到pool中并置于栈顶。同时哨兵的地址也作为objc_autoreleasePoolPop方法的入参来寻找需要进行pop操作的page。

     autoreleaseFast (hotPage:当前正在使用的page)

    大体的逻辑是这样

    1. 通过hotPage()方法获取hotPage
    2. 当hotPage存在并且空间未满时将传入的obj添加到page中
    3. 如果hotPage满了,就另外新建一个page并将obj添加到里面
    4. 如果hotPage不存在,就创建一个hotPage并将obj添加到里面

    hotPage 

    通过tls(Thread Local Storage)获取hotPage

    page->add() 

    autoreleaseFullPage

    autoreleaseNoPage

    创建一个parent为nil的page并添加一个哨兵,之后再将传入的obj添加到page中。

    那么一个obj什么时候被添加到autorelease pool中的呢。只有当一个对象被调用了autorelease方法的时候,该对象才会被加入到autorelease pool中。在MRC的环境下,开发者需要进行手动调用,而ARC的环境下,非alloc/copy/mutablecopy创建的对象都是autorelease对象,若想将通过alloc/copy/mutablecopy创建的对象变为autorelease对象只需要创建时在前面添加__autorelease关键字。

    autorelease

    rootAutorelease

    rootAutorelease2 

    确保非TaggedPointer后调用page的autorelease方法,之后进入autoreleaseFast流程成功将对象加入到autorelease pool中。

    objc_autoreleasePoolPop操作

    大体上的执行顺序包括pop() ==> releaseUntil(stop) ==> page->child->kill()

    也就是根据传入的stop不断释放对象直到stop的地址,之后开始kill掉childPage。

    而objc_autoreleasePoolPop调用时机就是当[NSAutoreleasePool release]的时候。

  • 相关阅读:
    直击 KubeCon 2019 现场,阿里云 Hands-on Workshop 亮点回顾
    分享 KubeCon 2019 (上海)关于 Serverless 及 Knative 相关演讲会议
    MaxCompute 费用暴涨之新增SQL分区裁剪失败
    UI2CODE复杂背景无法识别?闲鱼工程师这样打造高准确率方案
    阿里云发布边缘容器,云边端一体化时代来临
    中间件性能挑战赛上线了两大黑科技,是高手就盘它!!
    MaxCompute 费用暴涨之存储压缩率降低导致SQL输入量变大
    通知: Spring Cloud Alibaba 仓库迁移
    MaxCompute 项目子账号做权限管理
    性能压测工具选型对比
  • 原文地址:https://www.cnblogs.com/kaisi/p/10119095.html
Copyright © 2020-2023  润新知